第 53 题
#include <stdio.h>
void fun(char **p) {
++p;
printf("%s\n",*p);
}
int main(){
char *c[] = {"China","Beijing","Liaoning","Shenyang"};
fun(c);
return 0;
}
分析代码:
首先说一个字符串的概念,C语言里字符串主要是通过两种形式表现
字符数组,char类型数组。比如:char s[] = "hello";
字符指针,char类型指针。比如: char* s = "world"; //打个比方,不要在乎重名
char类型数组的字符串,数组内的各个元素都是char类型的变量,每个元素在内存中按顺序存储,比如元素 'h' 的地址是 42,那么元素 'e' 的地址则是紧随其后,char占1字节,所以 'e' 的地址则为 43 。我们知道数组名代表数组首地址,我们使用如下形式,可以查看验证一下。
char s[] = "hello";
printf("element:%c address:%d\n",s[0], s );
printf("element:%c address:%d\n",s[1], s+1);
printf("element:%c address:%d\n",s[2], s+2);
printf("element:%c address:%d\n",s[3], s+3);
printf("element:%c address:%d\n",s[4], s+4);
输出结果:
element:h address:6422042
element:e address:6422043
element:l address:6422044
element:l address:6422045
element:o address:6422046
char类型指针的字符串 char* s = "world",其实指针 s 中只存放了 'w'的地址。 s 毕竟是一个char* 类型的指针,它只能存储一个字符的地址。之所以可以这么赋值,是因为编译器为字段代码的 "world" ,先在内存中分配空间,并按把 'w' 、'o' 、'r' 、'l' 、'd' 、'\0' 按顺序存储 ,然后再把 'w' 的地址赋值给 指针 s。我们可以验证一下。
char* s = "world";
printf("element:%c address:%d\n",*s, s );
printf("element:%c address:%d\n",*(s+1), s+1);
printf("element:%c address:%d\n",*(s+2), s+2);
printf("element:%c address:%d\n",*(s+3), s+3);
printf("element:%c address:%d\n",*(s+4), s+4);
输出结果:
element:w address:4210688
element:o address:4210689
element:r address:4210690
element:l address:4210691
element:d address:4210692
上面虽然举例了,但我们平常字符串不是这么用的呀,我们不都是用 %s 配合输入输出函数来玩字符串的嘛? 那我们就来说说这个 %s, 我们上面用了 %c 和 %d ,%c 对应的参数类型是 char ,%d 对应的参数类型是 int ,这个%s 对应的参数类型则是一个char指针,也就是 char* 类型 (我试过参数是其他类型指针的话,虽没报错但也不好使)。咱们继续看,%s 她对应的是char指针,也就是字符串的第一个字符的地址。
我们接下来回到本题,给出了这个东西
char* c[];
可以看出,这是一个数组,他的每一个元素,都是一个char指针。在本题中,他是这么赋值的。
char *c[] = {"China","Beijing","Liaoning","Shenyang"};
那么如此我们就能看出她,实际上是编译器给"China"、"Beijing"这些字符串文本的每个字符,先去内存开辟空间,并且把他们按照顺序存进内存里面,然后把每个字符串文本的首字符的地址,赋值给数组的每个元素。也就是:把'C'的地址赋值给 c[0]、'B'的地址赋值给 c[1]、'L'的地址赋值给 c[2]、'S'的地址赋值给 c[3]。大概如下:
printf("%d",c[0]);输出的则是字符'C'的地址
printf("%c",*c[0]);输出的则是字符 'C'
printf("%s",c[0]);输出的则是字符串 s"China"
我们可以测试如下代码:
char *c[] = {"China","Beijing","Liaoning","Shenyang"};
printf("element:%c address:%d String:%s \n",*c[0],c[0],c[0]);
printf("element:%c address:%d String:%s \n",*c[1],c[1],c[1]);
printf("element:%c address:%d String:%s \n",*c[2],c[2],c[2]);
printf("element:%c address:%d String:%s \n",*c[3],c[3],c[3]);
运行结果如下:
element:C address:4210688 String:China
element:B address:4210694 String:Beijing
element:L address:4210702 String:Liaoning
element:S address:4210711 String:Shenyang
我们来看字符C 和字符 B 的地址,4210688 和 4210694 中间相差了 5 个地址,因为编译器把他们是按顺序存储的,所以这5个地址则对应了'h'、'i'、'n'、'a'、'\0'。也就是c[0]对应'C'的地址,那么c[0]+1 对应的就是'h'的地址了,以此类推,我们继续用代码测试一下。( \0 不显示,所以不做测试)
printf("%c %c %c %c %c\n",*c[0],*(c[0]+1),*(c[0]+2),*(c[0]+3),*(c[0]+4);
printf("%c %c %c %c %c %c %c\n",*c[1],*(c[1]+1),*(c[1]+2),*(c[1]+3),*(c[1]+4),*(c[1]+5),*(c[1]+6));
运行结果:
C h i n a
B e i j i n g
我想到此,char *c[ ] 这个东西我们已经了解了,不过这道题,把 c 作为参数,传递给函数,并且用了 char **p 来接收这个地址。疑问来了,这个小家伙是什么东西?
char** p2 这个指针 p2 叫做二级指针,既然他叫二级指针,那么char * p1是不是就是一级指针呢? 对,是的,就是这么叫的。
- 既然 char * p1他存放的是 char 类型字符变量的地址
- 那么char ** p2存放的就是char * 类型指针变量的地址
是不是有点俄罗斯套娃的意思。既然如此,如果是char *** p3 的话,,,算了我没触碰过这个不敢乱说。
上面我们得到了char ** p 获取到了 char * 类型的,我们回顾一下这道题:
void fun(char **p) {
++p;
printf("%s\n",*p);
}
int main(){
char *c[] = {"China","Beijing","Liaoning","Shenyang"};
fun(c);
return 0;
}
我们看到给函数传的是 c 的首地址, 也就是 p 获取到了 c 的第一个元素 c[0] 的地址,使 p 指向 c[0] ,之后执行代码 ++p 使 p 指向 c[1] ,p 中存储的更变为 c[1] 的地址。
接下来使用 %s ,输出 *p ,我们已知 p 中存储的是 c[1] 的地址,那么 *p 则等同于 c[1] ,所以说 如果我们在主函数里面使用同样的输出函数,把参数 p 换成 c[1] ,效果是一样的,上代码:
void fun(char **p) {
++p;
printf("%s\n",*p);
}
int main(){
char *c[] = {"China","Beijing","Liaoning","Shenyang"};
fun(c);
printf("%s\n",c[1]); //新增在此处
return 0;
}
输出结果:
Beijing
Beijing
既然如此,本题完事。
59题
#include <stdio.h>
struct node {
char data;
struct node *next;
};
typedef struct node NODETYPE;
int main(){
NODETYPE a,b,c,*h,*p;
a.data = 'A';
b.data = 'B';
c.data = 'C';
h = &a;
a.next = &b;
b.next = &c;
c.next = '\0';
p = h;
while (p) {
printf("%c",p->data);
p = p->next;
}
printf("\n");
return 0;
}
main函数里,首先定义了结构变量 a,b,c ,还有结构指针变量 h , p
NODETYPE a,b,c,*h,*p;
NODETYPE a,b,c,*h,*p; 这句代码这里之所以用了NODETYPE 就可以定义这些变量,是因为事先用了此代码 typedef struct node NODETYPE; ,之后使用NODETYPE,效果就等同于struct node ,具体内部有啥不同,先不研究。
typedef struct node NODETYPE;
NODETYPE a,b,c,*h,*p;
struct node a,b,c,*h,*p; //效果相同
接下来是赋值,结构变量a b c 的成员 data 分别赋值上大写字母ABC
a.data = 'A';
b.data = 'B';
c.data = 'C';
把结构变量 a 的地址赋值给结构指针 h。结构变量 b 的地址赋值给结构变量 a 的成员指针next,也就是赋值给a.next。把结构变量 c 的地址赋值给b.next。
h = &a;
a.next = &b;
b.next = &c;
我们上面说的都很容易理解,而下面这个语句,则比较迷惑,指针不止是能赋值地址么, '\0' 我们得说法是标记C字符串结尾的字符,或者称它为空字符,给指针赋值一个字符怎么回事?下面给出解答。
c.next = '\0'
我们这道题意思其实是想让 c.next 成为一个空指针,空指针是一个不指向任何地方的指针,但不对呀,空指针应当赋值 NULL,而不是'\0'呀,空指针和空字符怎么能混淆么?这么写对么?不能混淆,不对,但是也能用......只是偶尔会出点问题,程序嘛,能跑就行又不是不能用(溜了溜了
这道题目的是,如果while( )中检查到指针== 0 (检测为空指针的方法),也就是他是个空指针,就退出循环,就这么点用处,咱也不能让出卷的改成NULL,所以好使就行。
接下来就是把 h 中的值,赋值给 p ,h中存储的是 a 的地址。之后进入循环
p = h;
while (p) {
printf("%c",p->data);
p = p->next;
}
printf("\n");
return 0;
首先while检查一下 p 是不是空指针,不是,进入第一次循环:
- 输出 p -> data,此时 p 指向 a,我们看此时a.data是何物即可,输出了'A'
- 把 p->next 赋值给 p ,这时候 p 还指向 a ,a.next 中是b的地址,也就是 p 接下来开始指向 b 了。(本次循环完毕)
再检查一下 p 是不是空指针,指向 b 呢,不是,进入第二次循环:
- 输出 p -> data,此时 p 指向 b,输出了'B'
- 把 p->next 赋值给 p ,b.next 中是c的地址,也就是 p 接下来开始指向 c了。(本次循环完毕)
指向 c 呢 也不是空指针,第三次循环:
- 输出 p -> data,此时 p 指向 c,输出了'C'
- 把 p->next 赋值给 p ,c.next 是一个空指针啊,现在 p 也光荣成为一个空指针了。(本次循环完毕)
这时候循环结束了,换了个行,就return 0了,总之,输出的内容是 ABC
ABC
第60题
#include <stdio.h>
struct student{
int num;
char name[20];
}stu[2] = { {170101,"WangHong"},{170102,"LiMIng"} };
int main(){
struct student *p;
printf("ID NAME\n");
for(p = stu; p < stu+2; p++) {
printf("%d %s\n",p->num,p->name);
}
return 0;
}