C语言程算课:指针与结构体的一些理解

1|0指针:


C语言指针运算中,&是取地址符号,*是解地址符号,是一对逆运算,对于一个变量a和一个指针p有下面两式成立:

*(&a) = a; &(*p) = p;

我们一般认为指针的定义类型是 int* ,因此int* p表示定义指针p,* 的位置随意,可以写成int *p。

这里p就是指针,而不是说*p是指针。

打印地址:

int main() { int a = 5; printf("%p %p %d %d\n", &a, a, &a, a); return 0; } //output: 0062ff1c 00000005 6487836 5 //%p用于打印地址,%d也能输出,实质上都是输出整型。 //但%p优点是以16进制数字输出,并且自动补全到8位,共有16^8=2^32位地址。 int main() { int a = 5, b = 8; printf("%p %p %p %d", &a+1, (&a)+1, &a, *(&a+1) ); return 0; } //output: 0062ff1c 0062ff1c 0062ff18 8

上面的输出结果解释:&和*作为指针运算符,优先级比加减乘除高,所以第一个和第二个写法输出是一样的,一个int型占四个字节,因此加一地址加4(对比输出三、四)
在栈空间中连续定义的变量地址是连续的,&a+1是指向b的指针,解地址可以得到b的值(不建议这么写,这是一种越界行为)。

下面的例子,继续帮助理解优先级不同导致的输出结果不同:

int main() { int a[6]; int* p = a; //a为一个数组时,a本身就是地址,不用加&。 a[0] = 114514; a[1] = 1919810; printf("%d %d\n", *(p+1), *p+1); return 0; } //output: 1919810 114515 //第一个是输出了a[1],第二个是输出了a[0]+1。

既然提到数组那就补充些二维数组与指针的知识:

int a[5][5]; int* p; p = &a[3][2]; //单个元素取地址是指针。 p = a[3]; //a加上第一维,就是a[3]这一行五个元素的指针,a[3]是五个元素的第一个元素地址。直接赋值,不用取地址。 p = *a; //a什么都不加,那就是指针的指针(这东西是可以嵌套的)。对a取地址,就是第一行第一个元素的地址。 printf("%d", *(*a) ); //对a取两次地址,输出第一个元素的值。 printf("%d", *(*(a+2)+3) ); //取出第三行第四个元素。

下面是二维数组的指针:

int* p1[8]; //这是定义了8个int型指针 int (*p2) [5]; //这是定义了一个指向数组的指针(数组长度为5),因为数组的返回值本身就是指针,所以这个写法就可以理解为指针的指针了(就是和"a"是同一层的)。 //赋值方式(对比上一个代码): p1[1] = &a[0][0]; p1[2] = a[0]; p1[3] = *a; //这是p1指针数组中的三个不同的指针,但是它们值是一样的。 //p1是个数组,但是p2只有一个。 p2 = &a[0]; p2 = a; //两个式子等价。 printf("%d", *(*(p2 + 2)+3) ); //与上一份代码相同,都表示第三行第四个元素。

2|0结构体:


&与*优先级相同,而结构体的成员选择符号“.”的优先级要高于两者,当定义一个结构体指针并读取指向的成员时,应有如下写法:

(*p).data = a; //*p.data = a; 会编译错误

↓结构体与指针的详细用法:

struct Node{ int data; char fu; char *name; int shuzu[]; }dian1 = {2, 'g', "this", {0,1,2,3,4} }, dian2 = {3, 'g', "this is a test name", {} }; struct Node *p1 = &dian1, *p2 = &dian2; //结构体指针; int main() { dian1.data = 2333; (*p1).data = 2333; p1->data = 2333; //三个式子等价; printf("%d %d\n", dian1.data, dian1.shuzu[2]); printf("%p %p %c %c\n", p2->name, (*p2).name, *((*p2).name), *((*p2).name+3) ); return 0; } /* output: 2333 2 0040c049 0040c049 t s */

char指针可以用字符串赋值,指针保存的是第一个字符的地址,接地址可以得到第一个字符,通过加上一个常数获得字符串第i个字符。

3|0typedef 替换:


typedef struct Dian{ int zhi; }D, E, *P; int main() { D a = {5}; E b = {6}; //虽然结构体内只有一个整型,但还是要括起来。 P p = &a; printf("%d %d ", p->zhi, a.zhi); //*P指针用"->",不是指针用"." b = a; //a和b分别用D和E来定义,但是本质上都是Dian,是同一个类型。 printf("%d", b.zhi); return 0; }

补充一些typedef用法,typedef可以替换数据类型,就是为复杂的声明定义简单的别名,与宏定义有一些差异。本身是一种储存类的关键字,与auto,extern,mutable,static,register等关键词不能出现在同一个表达式。

下面是详细用法:

typedef double db; //与宏定义不同,typedef是一个语句,末尾要加";" db pi = 3.1415926; //db直接当double用。 typedef unsigned long long ull; //比较常用的写法,定义起来方便了很多。 //这里补充一个问题,就是一定要用对应的格式输出结果,否则会出错。 //举个例子: ull t = 2; unsigned int r = 2; printf("%llu %llu\n",t,r); //llu是输出无符号long long //output: 2 18042985811804162 //输出错误是因为ull和ul占的字节长度不同,输出时地址越界了,这个是ub问题,不同编译器输出结果不同,不必要追究原因,只要注意这个问题就好。 //正确输出: printf("%llu %lu\n",t,r); typedef int array [4]; array a; a[2] = 2333; //array可以直接当成定义一个长度为4的数组来用。 typedef int array2 [5][6]; array2 b; b[3][1] = 114514; //array2可以直接当定义二维数组来用。 typedef int* pointer; pointer p; //用pointer相当于定义指针。 typedef int* pointer2 [5]; pointer p2; //定义指针数组;

不定期补充哦。

------update : 2023.03.07


__EOF__

本文作者枫叶晴
本文链接https://www.cnblogs.com/maple276/p/17399745.html
关于博主:菜菜菜
版权声明:呃呃呃
声援博主:呐呐呐
posted @   maple276  阅读(114)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示