前言:
在C语言的学习及使用中,包括阅读github上的各种源代码都少不了绕得人眼花缭乱的指针。本文简单讨论一下指针及变量的关系,希望能起到抛砖引语的作用,和大家一起探讨学习。
环境:
- 编译器:Clang
- 编译命令:gcc test_main.cpp -o test_main
- 操作系统:MacOS
内容:
普通变量
Code:
int main(int argc, char* argv[]){
int a;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 0
return 0;
}
OutPut:
&a = 1547790796 a = 0
这里我们剋看到变量a已经被分配了内存,这块内存的地址为‘1547790796’,内存中存储着一个整形数字0。
接下来我们给这个变量赋值
Code:
int main(int argc, char* argv[]){
int a;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 0
a = 723;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 723
return 0;
}
OutPut:
&a = 1547790796 a = 0
&a = 1547790796 a = 723
由于已经被在内存中开辟了一块区域,所以直接赋值操作并没有问题。
指针
Code:
int main(int argc, char* argv[]){
int a;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 0
a = 723;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 723
int *b;
printf("&b = %d b = %d\n",&b,b); //&b = 1547790784 b = 0
b = &a;
printf("&b = %d b = %d *b = %d\n",&b,b,*b); //&b = 1547790784 b = 1547790796 *b = 723
return 0;
}
OutPut:
&a = 1547790796 a = 0
&a = 1547790796 a = 723
&b = 1547790784 b = 0
&b = 1547790784 b = 1547790796 *b = 723
我们发现在声明了指针之后并不会分配内存,指针直接指向了地址0,所以在这个时候使用静态语句直接初始化会导致程序运行时异常中断(eg: *b = 723,访问无权限操作的内存,在程序的运行状态中,在可访问内存中的越界行为不会导致程序中断,而访问了无权限内存时则会被操作系统终止)
当我们把a的地址赋给b之后,*b就可以正式的代表一个int类型的变量了,而&b则是这个指针的地址,也就是一个 **int 类型的变量。在之后的程序中我们会看到不论是第几重指针,指针本身都有一个地址,而指针本身在声明时并未被初始化,所以指针有指针本身的地址,却指向了不可操作的一块内存,只有当指针指向了一个初始化的地址之后,指针本身才可用于访存。
Code:
int main(int argc, char* argv[]){
int a;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 0
a = 723;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 723
int *b;
printf("&b = %d b = %d\n",&b,b); //&b = 1547790784 b = 0
b = &a;
printf("&b = %d b = %d *b = %d\n",&b,b,*b); //&b = 1547790784 b = 1547790796 *b = 723
int **c;
printf("&c = %d c = %d\n",&c,c); //&c = 1547790776 c = 0
c = &b;
printf("&c = %d c = %d *c = %d **c = %d\n",&c,c,*c,**c); //&c = 1547790776 c = 1547790784 *c = 1547790796 **c = 723
int ***d;
printf("&d = %d d = %d\n",&d,d); //&d = 1547790768 d = 0
d = &c;
printf("&d = %d d = %d *d = %d **d = %d ***d = %d\n",&d,d,*d,**d,***d); //&d = 1547790768 d = 1547790776 *d = 1547790784 **d = 1547790796 ***d = 723
return 0;
}
OutPut:
&a = 1547790796 a = 0
&a = 1547790796 a = 723
&b = 1547790784 b = 0
&b = 1547790784 b = 1547790796 *b = 723
&c = 1547790776 c = 0
&c = 1547790776 c = 1547790784 *c = 1547790796 **c = 723
&d = 1547790768 d = 0
&d = 1547790768 d = 1547790776 *d = 1547790784 **d = 1547790796 ***d = 723
指针及指针指向内存的地址
当我们要初始化指针时,可以选择使用malloc函数。
Code:
int main(int argc, char* argv[]){
int a;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 0
a = 723;
printf("&a = %d a = %d\n",&a,a); //&a = 1547790796 a = 723
int *b;
printf("&b = %d b = %d\n",&b,b); //&b = 1547790784 b = 0
b = &a;
printf("&b = %d b = %d *b = %d\n",&b,b,*b); //&b = 1547790784 b = 1547790796 *b = 723
int **c;
printf("&c = %d c = %d\n",&c,c); //&c = 1547790776 c = 0
c = &b;
printf("&c = %d c = %d *c = %d **c = %d\n",&c,c,*c,**c); //&c = 1547790776 c = 1547790784 *c = 1547790796 **c = 723
int ***d;
printf("&d = %d d = %d\n",&d,d); //&d = 1547790768 d = 0
d = &c;
printf("&d = %d d = %d *d = %d **d = %d ***d = %d\n",&d,d,*d,**d,***d); //&d = 1547790768 d = 1547790776 *d = 1547790784 **d = 1547790796 ***d = 723
b = (int*)malloc(sizeof(int*)*1);
*b = 327;
printf("&b = %d b = %d *b = %d\n",&b,b,*b); //&b = 1547790784 b = 1740636288 *b = 327
return 0;
}
OutPut:
&a = 1547790796 a = 723
&b = 1547790784 b = 0
&b = 1547790784 b = 1547790796 *b = 723
&c = 1547790776 c = 0
&c = 1547790776 c = 1547790784 *c = 1547790796 **c = 723
&d = 1547790768 d = 0
&d = 1547790768 d = 1547790776 *d = 1547790784 **d = 1547790796 ***d = 723
&b = 1547790784 b = 1740636288 *b = 327
我们发现malloc分配出来的地址和其他的地址差距时比较大的,这是因为在函数中声明的临时变量被存储在栈当中,而通过mallloc分配的内存则在堆中。而存储在栈那的变量发生越界时则基本上都会踩到已经分配出去的内存。
我们知道栈区内存是连续分配的,入栈出栈的操作仅仅改变了栈顶指针,所以,当我们向栈区连续压入数据的时候,内存地址也必然是连续的,但是在这里我们发现&a到&b有12个字节的长度,而其他的变量之间长度都是8个字节,这是因为只有变脸a在声明时被分配了4个字节的存储空间存放这个int类型的值,再加上指针本身的8个字节,正好12个字节。