一.左值和右值
赋值号左边的叫左值,右边的叫右值
当一个变量做左值时,编译器认为这个变量符号的真是含义是这个变量所对应的那个内存空间;当一个变量做右值时,编译器认为这个变量符号的真实含义是这个变量的值,也就是这个变量对应的内存空间中存储的那个数。
int a = 3, b =5;
a = b; //当a做左值时,我们关心的是a所对应的内存空间,而不是其中存储的3
b = a; //当a做右值时,我们关心的是a所对应空间中存储的数,也就是5
二.野指针
野指针因为指向的地址是不可预知的,所以有3种情况:
第一种:指向不可访问(操作系统不允许访问的敏感地址,譬如内核空间)的地址,结果是触发段错误
第二种:指向一个可用的、而且没什么特别意义的空间(譬如我们曾经用过但是自己已经不用的栈空间或堆空间),这样错误被掩盖,但是用法不对。
第三种:指向一个可用的空间,而且这个空间其实在程序中正在被使用,野指针引用可能修改某个变量的值,造成离奇的错误。可能使程序崩溃
避免出现野指针:
定义指针时,同时初始化为NULL
使用完成后,同时赋值为NULL
三.const修饰的变量真的不能修改么
其实可以修改,用指针间接操作即可(前提是在gcc环境下)
修改a在内存中的值
const int a = 5;
int *p = (int *)&a;
*p = 6;
修改指针p指向的地址
int a = 5;
int b =4;
int *const p = &a;
printf("p:0x%x---&a:0x%x-----&b:0x%x\n",p,&a,&b);
int **q = (int **)&p;
*q = (int *)&b;
printf("p:0x%x---a:0x%x-----b:0x%x\n",p,&a,&b);
p:0xbfe834d0---a:0xbfe834d0-----b:0xbfe834d4
p:0xbfe834d4---a:0xbfe834d0-----b:0xbfe834d4
const在gcc中是通过编译器在编译时检查来确保实现的,所以我们骗过编译器就可以修改const变量
gcc把const类型的常量也放在了data段,其实和普通的全局变量放在data段是一 样实现的,只是通过编译器来认定这个变量是const的,运行时并没有标记const标志,所以只要骗过编译器就可以了
四.sizeof和strlen
特别注意:32位系统中,所有指针长度都是4,不管是什么类型
char str[] = "hello";
char *P = str;
sizeof(p); //4 相当于sizeof(char *) 32位系统中,所有指针长度都是4,不管是什么类型
sizeof(*p); //1 相当于sizeof(char)
strlen(p); //5 相当于strlen(str)
strlen是一个C函数库,用来返回一个字符串的长度,注意strlen接收的参数必须是一个字符串(字符串以'\0'结尾)
void func(int b[100])
{
sizeof(b)
//4 函数传参,形参是可以用数组来的,函数形参是数组时,实际传递的不是整个数组,而是数组的首元素的首地址。也就是说函数传参用数组来传,实际上相当于传递的是指针(指针指向数组的首元素的首地址)
}
函数传参,形参是可以用数组来的,函数形参是数组时,实际传递的不是整个数组,而是数组的首元素的首地址。也就是说函数传参用数组来传,实际上相当于传递的是指针(指针指向数组的首元素的首地址)
五.指针与函数传参
5.1普通变量作为函数形参
void func1(int b)
{
//在形参内部,形参的值等于实参,原因是函数调用时把实参的值传递给形参。
}
int main(void)
{
int a =4;
func1(a);
}
//&a和&b不同,说明a和b不是同一个变量(在内存中a和b是独立的2个内存空间)
//但是a和b是有关联的,实际上b是a赋值得到的
5.2 数组作为函数形参
(1)数组名作为形参传惨时,实际传递值不是整个数组,而是数组的首元素的首地址(也就是整个数组的首地址)。所以在子函数内部,传进来的数组名就等于是一个指向数组首元素首地址的指针。sizeof算出来就是4.
(2)在子函数内传参得到的数组首元素首地址,和外面得到的数组首元素首地址的值是相同的。很多人把这种特性叫做“传址调用”(所谓的传址调用就是调用子函数时传了地址(就是指针)),此时可以通过传进去的地址访问实参。
(3)数组作为函数形参时,[]里的数字是可有可无的。为什么?因为数组名做形参传递的实际只是个指针,根本没有数组长度这个信息。
5.3 指针作为函数形参
和数组作为函数形参是一样的,这就好像指针访问数组元素和数组访问数组元素一样
5.4 结构体变量作为函数形参
(1)实际上和普通变量(类似于int之类的)传参时表现是一模一样的。所以结构体变量其实也是普通变量而已。
(2)因为结构体一般都很大,所以如果直接用结构体变量进行传参,那么函数调用效率就会很低。(因为在函数传参时需要将实参赋给形参,所以当传参的变量越大调用效率就越低。解决办法最好就是改传变量的指针进去)
(3)结构体因为自身太大,所以传参应该用指针来传
总结:
1.普通变量(包括结构体)传递函数参数,实际上就是赋值
2.数组(包括指针)传递函数参数,实际上传递的是地址,数组传递的是数组首元素的地址。