1.指针实际应用4:多级指针的使用
如果需要修改指针本身的指向,那么在函数传参时,必须使用地址传递。
相关代码参考demo1_二级指针的应用.c
2.指针实际应用5:空指针、野指针介绍
空指针:
指向NULL地址(0x00000000)的指针。(该块内存为不可访问区,如强行访问将导致段错误。)
Segmentation fault (core dumped)
备注:
- 段错误不属于语法错误,编译器是不会提示的。
出现原因:一般原因是非法内存操作(访问不可访问区域,访问越界,内存溢出。。。)
解决办法:可在关键位置添加打印语句,找出错误出现的代码行。
printf("[%d]\n", __LINE__);
野指针:
指向不明确的指针,任意使用有可能导致不可预料的错误。
备注:
定义一个指针后,最后是立即对其进行指向。
如果暂时没有明确指向,可直接指向NULL。(至少不会修改宝贵数据,数据是无价的。)
3.指针实际应用6:字符串操作函数
#include <string.h>
1.strlen :计算字符串长度
size_t strlen(const char *s);
2.strcmp :比较字符串s1和s2(相同返回0,不同返回非0)
int strcmp(const char *s1, const char *s2);
3.strcpy :将源字符串src复制到目标字符串dest
char *strcpy(char *dest, const char *src);
4.strcat :将源字符串src拼接到目标字符串dest之后。
char *strcat(char *dest, const char *src);
指针习题
1 、设有定义: int a,*pa=&a; 以下 scanf 语句中能正确为变量 a 读入数据的是()
A) scanf("%d", pa); B) scanf("%d", a);
C) scanf("%d", &pa); D) scanf("%d", *pa);
2 、若有以下定义和语句
int main() { int a=4, b=3, *p, *q, *w; p=&a; q=&b; w=p; q=NULL; }
则以下选项中错误的语句是()
A) *q=0; B) w=p; C) *p=a; D) *p=*w;
3 、有以下程序
int main() { int a=7, b=8, *p, *q, *r; p=&a; q=&b; r=p; p=q; q=r; printf("%d,%d,%d,%d\n", *p, *q, a, b); }
程序运行以后的输出结果是 ()
A) 8,7,8,7 B) 7,8,7,8
C) 8,7,7,8 D) 7,8,8,7
4 、程序中对 fun 函数有如下说明
void *fun();
此说明的含义是 ( )
A) fun 函数无返回值
B) fun 函数的返回值可以是任意的数据类型
C) fun 函数的返回值是无值型的指针类型
D) 指针 fun 指向一个函数,该函数无返回值
5 、有以下程序
int *f(int *x,int *y) { if(*x<*y) return x; else return y; } main() { int a=7, b=8, *p, *q, *r; p=&a; q=&b; r=f(p,q); printf("%d,%d,%d\n", *p, *q, *r); }
执行后输出结果是 ( )
A) 7,8,8 B) 7,8,7 C) 8,7,7 D) 8,7,8
6 、设有定义语句
int x[6]={2,4,6,8,5,7}, *p=x, i;
要求依次输出 x 数组 6 个元素中的值,不能完成此操作的语句是 ()
A) for(i=0;i<6;i++) printf("%2d",*(p++));
B) for(i=0;i<6;i++) printf("%2d",*(p+i));
C) for(i=0;i<6;i++) printf("%2d",*p++);
D) for(i=0;i<6;i++) printf("%2d", (*p)++);
备注: (D) 中 (*p)++ 是元素值加 1 ,而不是指针值加 1
7 、有以下程序
main() { int x[8]={8,7,6,5,0,0}, *s; s=x+3; printf("%d\n", s[2]); }
执行后的输出结果是 ( )
A) 随机值 B) 0 C) 5 D) 6
8 、有以下程序段
int a[10]={1,2,3,4,5,6,7,8,9,10}, *p=&a[3], b;
b=p[5];
b 中的值是 ( )
A) 5 B) 6 C) 8 D) 9
9 、有以下程序
void fun(int *a,int i,int j) { int t; if(i<j){ t=a[i]; a[i]=a[j]; a[j]=t; fun(a,++i,--j); } } main() { int a[]={1,2,3,4,5,6}, i; fun(a,0,5); for(i=0; i<6; i++) printf("%d", a[i]); }
执行后的输出结果是 ( )
A) 6 5 4 3 2 1 B) 4 3 2 1 5 6
C) 4 5 6 1 2 3 D) 1 2 3 4 5 6
10 、有以下程序
void swap1(int c0[], int c1[]) { int t; t=c0[0]; c0[0]=c1[0]; c1[0]=t; } void swap2(int *c0, int *c1) { int t; t=*c0; *c0=*c1; *c1=t; } main() { int a[2]={3,5}, b[2]={3,5}; swap1(a, a+1); swap2(&b[0], &b[1]); printf("%d %d %d %d\n", a[0], a[1], b[0], b[1]); } )
A ) 3 5 5 3 B ) 5 3 3 5
C ) 3 5 3 5 D ) 5 3 5 3
11 、有以下程序
main() // 指向包含 3 个元素的一维数组的指针变量 { int a[][3]={{1,2,3},{4,5,0}}, (*pa)[3], i; pa=a; for(i=0; i<3; i++) { if(i<2) pa[1][i]=pa[1][i]-1; else pa[1][i]=1; } printf("%d\n", a[0][1]+a[1][1]+a[1][2]); }
执行后输出结果是 ( )
A) 7 B) 6 C) 8 D) 无确定值
12 、若有以下说明和语句:
int c[4][5], (*p)[5];
p=c;
能正确引用 c 数组元素的是 ( )
A) p+1 B) *(p+3) C) *(p+1)+3 D) *(p[0]+2)
13 、有以下程序
main() { int a[3][3], *p, i; p=&a[0][0]; for(i=0;i<9;i++) p[i]=i+1; printf("%d\n", a[1][2]); }
程序执行以后的输出结果是 ( )
A) 3 B) 6 C) 9 D) 7
14 、有以下程序
main() { char s[]="Yes\n/No", *ps=s; puts(ps+4); *(ps+4)=0; puts(s); }
程序运行后的输出结果是 ( )
A) n/No B) /No C) n/No D) /No
Yes Yes Yes /No
/No /No Yes
15 、有以下程序
main() { char str[][20]={"Hello", "Beijing"}, *p=str[0]; printf("%d\n", strlen(p+20)); }
程序运行后的输出结果是 ( )
A) 0 B) 5 C) 7 D) 20
16 、有以下程序
main() { char s[]="ABCD", *p; for(p=s+1; p<s+4; p++) printf("%s\n", p); }
程序运行后的输出结果是 ( )
17 、以下程序运行后的输出结果是 ( )
main() { char a[]="Language", b[]="Programe"; char *p1,*p2; int k; p1=a; p2=b; for(k=0; k<8; k++) if(*(p1+k)==*(p2+k)) printf("%c", *(p1+k)); }
18 、有以下程序
main() { char *p[10]={"abc", "aabdfg", "dcdbe", "abbd", "cd"}; printf("%d\n", strlen(p[4])); }
执行后输出结果是 ()
A) 2 B) 3 C) 4 D) 5
19 、若有定义: int *p[3]; ,则以下叙述中正确的是 ()
A) 定义了一个基类型为 int 的指针变量 p ,该变量具有 3 个指针。
B) 定义了一个指针数组 p ,该数组含有 3 个元素,每个元素都是基类型为 int
的指针。
C) 定义了一个名为 *p 的整型数组,该数组含有 3 个 int 类型元素。
D) 定义了一个可指向一维数组的指针变量 p ,所指一维数组应具有 3 个 int 类
型元素。
指针习题答案:
1~5: AACCB
6~10: DBDAD
11~15: ADBBC
16:
BCD
CD
D
17:gae
18: 2
19: B
选A。
int a, *pa=&a;
// 指针pa指向变量a
scanf中的%d,表示需要输入数据到整型变量中。
(int *)
A) scanf("%d", pa); // int *,正确
B) scanf("%d", a); // int,类型不匹配
C) scanf("%d", &pa); // int **,类型不匹配
D) scanf("%d", *pa); // int,类型不匹配
2.选A
int a=4, b=3, *p, *q, *w; p=&a; // p -> a q=&b; // q -> b w=p; // w -> a q=NULL; // q -> NULL
A) *q=0; // 错误。访问了不可访问区域
B) w=p; // 正确。因指针p存储了a地址,使指针w指向a
C)*p=a; // 正确。a = a
D) *p=*w; // 正确。a = a
3.选C
int a=7, b=8, *p, *q, *r; p=&a; // p -> a q=&b; // q -> b r=p; // r -> a p=q; // p -> b q=r; // q -> a printf("%d,%d,%d,%d\n", *p, *q, a, b);
4.选C
返回值为void *的函数(指针函数)
A) fun 函数无返回值 //错误,void fun();
B) fun 函数的返回值可以是任意的数据类型 // 错误,只能是任意的指针类型。
C) fun 函数的返回值是无值型的指针类型(任意类型的指针)
D)指针 fun 指向一个函数,该函数无返回值 // 错误。不是指针。void (*fun)(void)
5. 选B
int a=7, b=8, *p, *q, *r; p=&a; // p -> a q=&b; // q -> b r=f(p,q); // 地址传递(返回最小值的地址) r -> a printf("%d,%d,%d\n", *p, *q, *r);
6. 选D
A)for(i=0;i<6;i++) printf("%2d",*(p++)); // ++优先级高于*,与C相同
B) for(i=0;i<6;i++) printf("%2d",*(p+i)); // 与AC不同,指针p本身不偏移。
C) for(i=0;i<6;i++) printf("%2d",*p++);
D) for(i=0;i<6;i++) printf("%2d", (*p)++); // 错误。因为操作元素值++
p++ => p=p+1; p+=1;
7.选B
int x[8]={8,7,6,5,0,0}, *s; s=x+3; // s -> 数据5 printf("%d\n", s[2]); // s[2]=*(s+2)
8.选D
// p -> 数据4 int a[10]={1,2,3,4,5,6,7,8,9,10}, *p=&a[3], b; b=p[5]; // *(p+5) 数据9
9.选A
// 1: &a[0] 0 5 // 2: &a[0] 1 4 // 3: &a[0] 2 3 // 4: &a[0] 3 2 void fun(int *a,int i,int j) { int t; // 1:0<5 ,逻辑真,成立 // 2:1<4 ,逻辑真,成立 // 3:2<3 ,逻辑真,成立 // 4:3<2 ,逻辑假,不成立 if(i<j) { t=a[i]; a[i]=a[j]; a[j]=t; // 1:6 2 3 4 5 1 // 2:6 5 3 4 2 1 // 3:6 5 4 3 2 1 fun(a,++i,--j); // 1: &a[0] 1 4 // 2: &a[0] 2 3 // 3: &a[0] 3 2 } } main() { // 0 1 2 3 4 5 int a[]={1,2,3,4,5,6}, i; fun(a, 0, 5); for(i=0; i<6; i++) printf("%d", a[i]); }
10.选D
swap1(a, a+1);
swap2(&b[0], &b[1]);
上述函数都是地址传递。
11,选A
// 2 + 4 + 1 printf("%d\n", a[0][1]+a[1][1]+a[1][2]);
12,选D
int c[4][5], (*p)[5];
p=c; // p -> c第1个子数组
能正确引用c数组int元素的是( )
A) p+1 // 数组地址
B) *(p+3) // 数组首元素地址
C) *(p+1)+3 // int元素地址
D) *(p[0]+2) // p[0][2]能够成功访问int
13,选B
// 0 1 2 //0: 1 2 3 //1: 4 5 <6> //2: 7 8 9
14,选B
// 指针s指向字符串常量(位于只读区),不可写入。 // char *s="Yes\n/No", *ps=s; // 数组s存储着字符串(合法内存,可读可写) // ps+4 char s[]="Yes\n/No", *ps=s; // 'Y' 'e' 's' '\n' '\0' 'N' 'o' '\0'(字符串结束符) printf("sizeof: %ld\n", sizeof(s)); printf("strlen: %ld\n", strlen(s)); puts(ps+4); // printf("%s\n"); *(ps+4)=0; // 将 '/' 修改为 '\0' puts(s); // printf("%s\n");
15,选C
// char [20] char [20] char str[][20]={"Hello", "Beijing"}, *p=str[0]; // str[0]->*str // p -> 'H'
16,输出:
BCD
CD
D
17.输出:gae
char a[]="Language", b[]="Programe"; char *p1, *p2; int k; p1=a; // p1 -> 'L' p2=b; // p2 -> 'P' // "Language" // "Programe" for(k=0; k<8; k++) // 0~7 if(*(p1+k)==*(p2+k)) // if(a[k] == b[k]) printf("%c", *(p1+k)); // gae
18,选A
// 指针数组 p[0] p[1] p[2] p[3] p[4] char *p[10]={"abc", "aabdfg", "dcdbe", "abbd", "cd"}; printf("%d\n", strlen(p[4])); *(p+4)
19.选B
int *p[3]; // 数组,长度为3,元素类型 int *
A) 定义了一个基类型为int的指针变量p,该变量具有3个指针。
B) 定义了一个指针数组p,该数组含有3个元素,每个元素都是基类型为int的指针。
C) 定义了一个名为*p的整型数组,该数组含有3个int类型元素。
D) 定义了一个可指向一维数组的指针变量p,所指一维数组应具有3个int类型元素。 ---> int (*p)[3]
demo1_二级指针的应用
#include<stdio.h> // 值传递(传递的是指针指向数据地址) void func_1(int *p) { p += 2; //p1 = p1+2; printf("func_1: p: %d\n", *p); } // 地址传递(传递的是指针本身的地址) // void func_2(int *(*p)) void func_2(int **p) // 也称为二级指针 { *p += 2; } int main() { int arr[5] = {1, 3, 5, 7, 9}; int *p1 = arr; // 指针p1指向数据1 printf("p1: %d\n", *p1); // func_1(p1); // 传递arr[0](数据1)地址 func_2(&p1); // 传递指针p1本身的地址 printf("p1: %d\n", *p1); return 0; }
demo2_空指针和野指针
#include<stdio.h> int main() { int *p1 = NULL; // printf("%d\n", *p1); int arr[5] = {1, 3, 5, 7, 9}; printf("[%d]\n", __LINE__); arr[454554654646546] = 100; // 问题出现在这里 printf("[%d]\n", __LINE__); printf("%d\n", arr[454554654646546]); printf("[%d]\n", __LINE__); int a; // 随机值 int *p1; // 野指针,也是随机值(可能是任意数据地址) // *p1 = xxxx; // 随意访问可能导致出现问题。 return 0; }
demo3_字符串操作函数
#include <stdio.h> #include <string.h> int main() { //1. strlen :计算字符串长度 char *str1 = "hello"; // 最后有结束符'\0' // printf("str1: %ld\n", sizeof(str1)); //错误,计算的是指针的大小 printf("str1: %ld\n", strlen(str1)); // 2. strcmp :比较字符串s1和s2 // char *str2; // 野指针,不能写入内容 // char str2[10] = {0}; // 清零数组 char str2[10]; // 暂时为随机值 bzero(str2, sizeof(str2)); // 清零数组 scanf("%s", str2); //(输入字符串不能大于10字节) if(strcmp(str1, str2) == 0) printf("Same!\n"); else printf("Diffrent!\n"); //3. strcpy :将源字符串src复制到目标字符串dest char str3[10] = {0}; strcpy(str3, str2); printf("str3: %s\n", str2); // 4. strcat :将源字符串src拼接到目标字符串dest之后。 char str4[10] = "123"; strcat(str4, str3); printf("str4: %s\n", str4); return 0; }
demo4_用户名判断
#include <stdio.h> #include <string.h> int main() { char str1[10]; // bzero(str1, strlen(str1)); // str1中还没有数据,长度为0 bzero(str1, sizeof(str1)); scanf("%s", str1); // char *str2 = "hello,"; // 错误的。str2指针指向字符串常量,不允许修改 char str2[20] = "hello,"; // 正确的。str2是数组,合法内存,可以存入数据。 if(strcmp(str1, "Tom") == 0) { strcat(str2, str1); printf("str2: %s\n", str2); } else { printf("ERROR\n"); } return 0; }
demo5_习题讲解
#include <stdio.h> #include <string.h> main() { // 指针数组 p[0] p[1] p[2] p[3] p[4] char *p[10]={"abc", "aabdfg", "dcdbe", "abbd", "cd"}; printf("%d\n", strlen(p[4])); *(p+4) }