C语言要点—— 指针、数组

*指针的本质

1、指针在本质上也是一个变量。
2、指针用于保存内存地址的值。
3、指针需要占用一定的内存空间,不同类型的指针占用的内存空间大小相同。

* *号的意义

1、在指针声明时,*号表示所声明的变量为指针。
2、在指针使用时,*号表示取指针所指向的内存空间中的值。

*传值调用与传址调用

1、函数调用的本质是将实参值复制到形参。
2、传值调用中实参、形参为各自函数的局部变量,则形参在函数内数值变化不会影响外部实参的变化。
3、传址调用中将复制的指针,故形参、实参的地址一样,形参的变化即实参的变化。
4、当一个函数体内部需要改变实参的值,则需要使用指针参数。另,指针适用于复杂数据类型作为参数的函数中。

*数组的概念

数组是相同数据类型的变量的有序集合。

*数组的大小

数组在一片连续的内存空间中存储元素。
数组元素的个数可以显示或隐式指定。
eg:
int a[5] = {1, 2}; //指定长度为5,后3个元素编译器初始化为0
int b[] = {1, 2}; //隐式指定长度为2

*数组地址与数组名

1、数组名代表数组首元素的地址。
2、数组的地址需要用取地址符&才能得到。
3、数组首元素的地址值与数组的地址值相同。但数组首元素的地址与数组的地址是两个不同的概念。

*数组名的盲点

1、数组名可以看做一个常量指针。
2、数组名“指向”的是内存中数组首元素的起始位置。
3、在表达式中数组名只能作为右值使用。
4、只有在下列场合中数组名不能看做常量指针:
数组名作为sizeof操作符的参数。
数组名作为&运算符的参数。
5、数组名其实并不是指针,在外部声明时不能混淆。

*编译器处理指针与数组的区别

1、处理指针:一次寻址操作。
2、处理数组:无寻址。

*指针的运算

1、指针是一种特殊的变量,与整数的运算规则为:
p+n;<----->(unsigned int)p + n*sizeof(*p);
结论:当指针p指向一个同类型的数组的元素时,p+1将指向当前元素的下一个元素;p-1将指向当前元素的上一个元素;
eg:
int a[5] = {1,2}; 则a[i] = *(a+i);或&a[i] = a+i;
2、指针之间只支持减法运算,且必须参与运算的指针类型必须相同。
p1 - p2;<---->((unsigned int)p1 - (unsigned int)p2) / sizeof(type);
注:只有当两个指针指向同一个数组的元素时,指针相减才有意义,其意义为指针所指元素的下标差。
当两个指针指向的元素不在同一个数组中时,结果未定义。
数组的访问:
以下标形式访问数组中的元素:
int main()
{
int a[5];
a[1] = 3;
a[3] = 5;
return 0;
}
以指针的形式访问数组中的元素:
int main()
{
int a[5];
*(a+1) = 3;
*(a+3) = 5;
return 0;
}

*指针的比较

1、指针可以进行关系比较:
< <= > >=
2、指针关系的前提是同时指向一个数组中的元素。
3、任意两个指针之间的比较运算(== !=)无限制。
eg:
char s[] = {'H','e','l','l','o'};
char* pBegin = s;
char* pEnd = s+ DIM(s);
char* p = NULL;
for(p = pBegin; p<pEnd; p++)
{
printf("%c", *p);
}

*a和&a的区别

1、a为数组是数组首元素的地址。
2、&a为整个数组的地址。
3、a和&a的意义不同其区别在于指针运算。
a+1--->(unsigned int)a + sizeof(*a)
&a+1---->(unsigned int)(&a) +sizeof(*&a)
=(unsigned int)(&a) +sizeof(a)

*数组参数

1、C语言中,数组作为函数参数时,编译器将其编译成对应的指针。
eg:
void f(int a[]);<--->void f(int* a);
void f(int a[5]);<--->void f(int* a);
2、一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来表示数组的大小。

*指针和数组的对比

1、数组声明时编译器自动分配一片连续内存空间。
2、指针声明时只分配了用于容纳指针的4字节空间。
3、在作为函数参数时,数组参数和指针参数等价。
4、数组名在多数情况可以看做常量指针,其值不能改变。
5、指针的本质是变量,保存的值被看做内存中的地址。

*C语言中的字符串

1、从概念上讲,C语言没有字符串数据类型。
2、C语言中使用字符数组来模拟字符串。
3、C语言的字符串是以’\‘结束的字符数组。
4、C语言中的字符串可以分配于栈空间、堆空间或只读存储区。
eg:
char s1[] = {'H', 'e', 'l', 'l', 'o'}; //栈空间
char s1[] = {'H', 'e', 'l', 'l', 'o', ’\'}; //栈空间
char* s3 = "Hello"; //只读存储区,其值不能被更改
char* s4 = (char*)malloc(6*sizeof(char)); //堆空间
s4[0] = 'H';
s4[1] = 'e';
s4[2] = 'l';
s4[3] = 'l';
s4[4] = 'o';
s4[5] = '\0';
free(s4);

*字符串长度

1、字符串的长度就是字符串所包含字符的个数。
2、C语言中的字符串长度指的是第一个‘\0'字符前出现的字符个数。
3、C语言中通过’\0'结束符确定字符串的长度。
eg:
size_t strlen(const char* s)
{
size_t length = 0;
assert(s);
while(*s++)
{
length++;
}
return length;
}

*不受限制的字符串函数

1、不受限制的字符串函数是通过寻找字符串的结束符‘\0'来判断长度:
字符串复制:char* strcpy(char* dst, const char* src);
字符串连接:char* strcat(char* dst, cosnt char* src);
字符串比较:int strcmp(const char* s1, const char* s2);
2、注意事项:
①、不受限制的字符串函数都是以’\0'作为结尾标记来进行的,因此输入参数中必须包含'\0'。
②、strcpy和strcat必须保证目标字符数组的剩余空间足以保持整个源字符串。
③、strcmp以0值表示两个字符串相等。
第一个字符串大于第二个字符串的时候返回值大于0。
第一个字符串小于第二个字符串的时候返回值小于0。

*长度受限的字符串函数

1、长度受限的字符串函数接收一个显示的长度参数用于限定操作的字符数。
字符串复制:char* strncpy(char* dst, const char* src, size_t len);
字符串连接:char* strncat(char* dst, const char* src, size_t len);
字符串比较:int strncmp(const char* s1, const char*s2, size_t len);
2、注意事项:
①、strncpy只复制len个字符到目标字符串。
当源字符串的长度小于len时,剩余的空间以‘\0'填充。
当源字符串的长度大于len时,只有len个字符会被复制,且它不会以’\0'结束。
②、strncat最多从源字符串中复制len个字符到目标字符串中。
strncat总是在结果字符串后面添加‘\0'。
strncat不会用’\0'填充目标字符串的剩余空间。
③、strncmp只比较len个字符是否相等。

*数组类型

1、数组的类型由元素类型和数组大小共同决定。
eg:
int array[5]的类型为int[5];
2、C语言中通过typedef为数组类型重命名:typedef type(name)[size];
eg:
typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
数组定义:AINT5 iArray;
AFLOAT10 fArray;

*数组指针

1、数组指针用于指向一个数组。
2、数组名是数组首元素的地址,但不是数组的起始地址。
3、通过取地址符&作用于数组名可以得到数组的起始地址。
4、可通过数组类型定义数组指针:ArrayType* pointer;
也可以直接定义:type (*pointer) [n];
pointer为数组指针变量名,type为指向的数组的类型,n为指向的数组的大小。

*指针数组

1、指针数组是一个普通的数组。
2、指针数组中每个元素为一个指针。
3、指针数组的定义:type* pArray [n];
type*为数组中每个元素的类型,pArray为数组名,n为数组大小。
eg:

*main函数的参数

1、main函数可以理解为操作系统调用的函数。
2、在执行程序的时候可以向main函数传递参数。
int main()
int main(int argc)
int main(int argc, char* argv[])
int main(int argc, char* argv[], char* env[])
argc:命令行参数个数
argv:命令行参数数组
env:环境变量数组

*指向指针的指针

1、指针变量的本质是一个变量,在内存中会占用一定的空间,因此可以定义指针来保持指针变量的地址值。
2、定义指向指针的指针,主要由于指针也同样存在传值调用与传址调用。
eg:
int main()
{
int a = 0;
int* p = NULL;
int** pp = NULL;
pp = &p;
*pp = &a;
return 0;
}

*二维数组

1、二维数组名在内存中以一维的方式排布。
2、二维数组中的第一维是一维数组。
3、二维数组中的第二维才是具体的值。

*数组名

1、一维数组名代表数组首元素的地址。
2、二维数组可以看做一维数组,二维数组中的每个元素都是同类型的一维数组,二维数组名同样代表数组首元素的地址,此时将二维数组看作一维数组。

*数组小结

1、C语言中只有一维数组,而且数组大小必须在编译期就作常数确定。
2、C语言中一维数组的元素可以是任何类型的数据,包括可以是另一个数组。
3、C语言中只有数组的大小和数组首元素的地址是编译器直接确定的。

*函数中的数组参数

等效关系:

*函数类型

1、C语言中的函数有自己特定的类型。
2、函数的类型由返回值、参数类型和参数个数共同决定。
eg:
int add(int i, int j)的类型为:int(int, int)
3、C语言中通过typedef为函数类型重命名:
typedef type name(parameter list);
eg:
typedef int f(int, int);

*函数指针

1、函数指针用于指向一个函数。
2、函数名是执行函数体的入口地址。
3、可通过函数类型定义函数指针: FuncType* pointer;
4、也可以直接定义:type (* pointer) (parameter list);
pointer为函数指针变量名,type为指向函数的返回值类型,parameter list为指向函数的参数类型列表。
5、函数名取不取&,没有区别,与数组名不同。

*回调函数

1、回调函数是利用函数指针实现的一种调用机制。
2、回调机制原理:
①、调用者不知具体事件发生的时候需要调用的具体函数。
②、被调函数不知何时被调用,只知道被调用后需要完成的任务。
③、当具体事件发生时,调用者通过函数指针调用具体函数。
3、回调机制的将调用者和被调函数分开,两者互不依赖。




















猜你喜欢

转载自blog.csdn.net/bulebin/article/details/79598883
今日推荐