今天是重点是指针&结构体题题目。🆗🆗🆗
初阶指针_指针的概念
1.使用指针打印数组内容
题目1 使用指针打印数组内容 写一个函数打印arr数组的内容,不使用数组下标,使用指针。 arr是一个整形一维数组。
//方法1 #include<stdio.h> void print(int arr[],int sz) { int* p = arr; int i = 0; for (i = 0; i < sz; i++) { //❌printf("%d ", *p + i);万一数组不是+1增长 printf("%d ", *(p + i)); //数组在内存中连续存放,所以产生连续的地址 } } int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); print(arr,sz); return 0; }
//方法2 #include<stdio.h> void print(int arr[], int sz) { int* p = arr; int i = 0; for (i = 0; i < sz; i++) { printf("%d ", *p); p++; } } int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); print(arr, sz); return 0; } //或者 #include <stdio.h> int main() { int arr[] = {1,2,3,4,5,6,7,8,9,10}; //在这里完成代码 // 分析:因为数组中存储的元素类型是int类型的,因此只要给一个int的指针, // 依次取索引数组中的每个元素即可 int* p = arr; // 数组名代表数组首元素的地址 for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i) { printf("%d ", *p); // *p: 取到p所指向位置的元素 ++p; // 获取p的下一个位置 } return 0; }
2.字符串逆序
题目2 字符串逆序 写一个函数,可以逆序一个字符串的内容 //前面博文我们讲解过思想,这里我们回顾一下以及其他方法
//方法1_指针 #include<stdio.h> #include<string.h> void Reverse(char arr[],int sz) { char* left = arr; char* right = arr+(sz-1);//sz-2(sizeof) while(left<right) { int tmp = 0; tmp = *left; *left =*right;//交换元素 //left=right交换地址是❌不可以 *right = tmp; left++; right--;//地址++ } } int main() { char arr[] = "abcdef"; int sz = strlen(arr); //int sz=sizeof(arr)/sizeof(arr[0]); Reverse(arr,sz);//逆置函数 //打印逆置数组 printf("%s", arr); return 0; }
//方法2_数组下标 #include<stdio.h> #include<string.h> void Reverse(char arr[], int sz) { int left = 0; int right = sz - 1;//sz-2(sizeof) while (left < right) { int tmp = 0; tmp = arr[left];//元素——指针 arr[left]= arr[right]; arr[right] = tmp; left++; right--;//元素下标++ } } int main() { char arr[] = "abcdef"; int sz = strlen(arr); //int sz=sizeof(arr)/sizeof(arr[0]); Reverse(arr, sz);//逆置函数 //打印逆置数组 int i = 0; for (i = 0; i <= sz; i++) { printf("%c", arr[i]); } //printf("%s", arr); return 0; }
///_数组下标_无论在不在函数都不能产生逆置的效果 //#include<stdio.h> //#include<string.h> //int main() //{ // char arr[] = "abcdef"; // int sz = strlen(arr); // //int sz=sizeof(arr)/sizeof(arr[0]); // char left = arr[0]; // char right =arr[sz - 1];//sz-2(sizeof) // while (left < right) // { // int tmp = 0; // tmp = left;//元素——指针 // left = right; // right = tmp; // left++;❌ // right--;❌//元素+1 // } // printf("%s", arr); // return 0; //} //void reverse(int arr[], int sz) //{ // int left =arr[0];//1 // int right = arr[sz - 1];//5 // while (left < right) // { // int tmp = 0; // tmp = left; // left = right; // right= tmp; // left++;❌ // right--;❌ // } //} //因为一艘进行逆置的时数组里面的元素,两两进行交换实现逆置 //这里的right和left是新的变量,新在栈区创建的空间,交换的是这两个变量。(或许试试指针) //并不会对数组元素产生什么影响 //要进行逆置的是数组里面的元素,两两进行交换实现逆置的 //这里的left right是新的变量,交换的是这两个变量,并不会对数组元素产生什么影响 //字符串数组可以使用下标_%c 不能+-计算。 只是把数组里面的元素值赋值给了left和right这两个局部变量 但是left和right并不代表数组里面的元素,你改变left和right也不会影响数组里面的元素值 会发现地址都是不一样的,这就说明它们存储在内存中的位置是不一样的, 改变left和right自然不会改变数组啦 int a=1; int b=a; b=3; //a和b完全是两个变量 改变b不会影响a的值 这和上面是一个道理哦
3.整形数组和字符串数组
分别写出 整形数组和字符串数组的输入和逆置和打印
整形 数组
//整形输入 #include<stdio.h> int main() { int m = 0; scanf("%d", &m);//输入整形数组的个数 int arr[1000]; int i = 0; for (i = 0; i < m; i++) { scanf("%d", &arr[i]); } return 0; } //或者 #include<stdio.h> int main() { int m = 0; scanf("%d", &m);//输入整形数组的个数 int arr[1000]; int i = 0; for (i = 0; i < m; i++) { scanf("%d", arr+i); } return 0; }
//逆置_数组 #include<stdio.h> int main() { int arr[5] = { 1,2,3,4,5 }; //逆置 int sz = sizeof(arr)/sizeof(arr[0]); //strlen不能计算整形 int left = 0; int right = sz - 1; while (left < right) { int tmp = 0; tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; left++; right--; } //打印 int i=0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; } //逆置_指针 #include<stdio.h> void reverse(int arr[],int sz) { int* left = arr; int* right = arr+(sz - 1); while (left < right) { int tmp = 0; tmp = *left; *left=*right; *right= tmp; left++; right--; } } int main() { int arr[5] = { 1,2,3,4,5 }; //逆置 int sz = sizeof(arr) / sizeof(arr[0]); //strlen不能计算整形 reverse(arr, sz); //打印 int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
//打印 #include<stdio.h> int main() { int arr[] = { 1,2,3,4,5 }; int i = 0; int* p = arr; for (i = 0; i < 5; i++) { printf("%d ", arr[i]); } printf("\n"); int j = 0; for (j = 0; j < 5; j++) { printf("%d ", *(arr + j)); //printf("%d ", *(p + j)); } }
字符串数组
//输入 #include<stdio.h> int main() { char arr[1000] = { 0 }; gets(arr); printf("%s", arr); }
关于字符串数组逆置上文有也是下标和指针同时可用。 注意一定是char
//打印 #include<stdio.h> int main() { char arr[] = "tsqxgd"; printf("%s ", arr); printf("\n"); int i = 0; for (i = 0; i < (sizeof(arr) / sizeof(arr[0]) - 1); i++) { printf("%c", arr[i]); } return 0; }
4.打印菱形
题目3 用C语言在屏幕上输出以下图案 //找规律" "和'*" /* 思路: 仔细观察图形,可以发现,此图形中是由空格和*按照不同个数的输出组成的。 上三角:先输出空格,后输出*,每行中 空格:从上往下,一行减少一个 *:2*i+1的方式递增 下三角:先输出空格,后输出*,每行中 空格:从上往下,每行多一个空格 *: 从上往下,按照2*(line-1-i)-1的方式减少,其中:line表示总共有多少行 按照上述方式,将上三角和下三角中的空格和*分别输出即可。 */
#include<stdio.h> int main() { int line = 0; scanf("%d", &line);//输入想要的行数 int i = 0; //注意从0开始到line-1行 //上半行 for (i = 0; i < line; i++)//上半行数循环 { //每一行的打印 int j = 0; //line-1是第一行-i循环第二三等行 for (j = 0; j < line - 1 - i; j++) { printf(" "); } for (j = 0; j < 2 * i + 1; j++) { printf("*"); } printf("\n"); } //下半行 for (i = 0; i < line-1; i++) { int j = 0; for (j = 0; j <= i; j++) { printf(" "); } for (j = 0; j < 2 * (line - 1 - i) - 1; j++) { printf("*"); } printf("\n"); } return 0; }
5.打印水仙花数
题目4 打印水仙花数 求出0~100000之间的所有“水仙花数”并输出。 “水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身, 如:153=1^3+5^3+3^3,则153是一个“水仙花数”。 /* 思路: 此题的关键在于只要知道判断一个数据是否为水仙花数的方式, 问题就迎刃而解。假定给定一个数据data,具体检测方式如下: 1. 求取data是几位数 2. 获取data中每个位置上的数据,并对其进行立方求和 3. 对data中每个位上的数据立方求和完成后,在检测其结果是否与data相等即可, 相等:则为水仙花数 否则:不是 具体实现参考以下代码。 */ 易错: 特别注意循环进入是否要/10 必须有tmp把i的值存起来防止发生变化
#include<stdio.h> #include<math.h> int main() { int i = 0; for (i = 0; i <= 999999; i++) { //判断几位数_字幂数 int count = 1; int tmp = i; int sum = 0; while (tmp/=10)//易错//进入就要判断/10 { count++; } //求字幂数的和 //易错计算和一定要初始化为原来i进入的数字 tmp = i; while(tmp)//循环条件易错 { sum += pow(tmp% 10, count); tmp /= 10; } if (sum == i) { printf("%d ", i); } } return 0; }
6.计算求和
题目5 计算求和 求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字, 例如:2+22+222+2222+22222 /* 思路: 通过观察可以发现,该表达式的第i项中有i个a数字,因此: 假设第i项为temp,则第i+1项为temp*10+a 具体参考以下代码 */
//数字a的前n项之和 #include<stdio.h> #include<math.h> int main() { int n = 0; int a = 0; scanf("%d %d",&a,&n); int count = 0; int i = 0; int k = 0; for (i = 0; i < n; i++) { k = k * 10 + a; count += k; } printf("%d", count); return 0; }
结构体
7.喝汽水问题
题目6 喝汽水问题 喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以喝多少汽水(编程实现)。 /* 方法1: 1. 20元首先可以喝20瓶,此时手中有20个空瓶子 2. 两个空瓶子可以喝一瓶,喝完之后,空瓶子剩余: empty/2(两个空瓶子换的喝完后产生的瓶子) + empty%2(不够换的瓶子) 3. 如果瓶子个数超过1个,可以继续换,即重复2 */ // 方法2: //按照上述喝水和用瓶子换的规则的话, //可以发现,其实就是个等差数列:money*2-1
//方法1 #include<stdio.h> int main() { int money = 0; scanf("%d", &money); //喝的汽水的总个数 int total = money; //空瓶 int empty = money; while (empty > 1) { total = total + empty / 2;//总的=起始+空瓶个数 empty = empty / 2+empty%2;//空瓶=起始/2+空瓶余数(易错) } printf("%d", total); return 0; }
//方法2 #include<stdio.h> int main() { int money = 0; scanf("%d", &money); if(money>0) printf("%d", 2 * money - 1);//找规律 return 0; }
8.程序死循环解释
题目7 程序死循环解释 VS开发环境调试下面的代码,画图解释下面代码的问题 #include <stdio.h> int main() { int i = 0; int arr[] = {1,2,3,4,5,6,7,8,9,10}; for(i=0; i<=12; i++) { arr[i] = 0; printf("hello bit\n"); } return 0; }
9.选择题总结tips
指针
- 在32位系统: int类型占4个字节,指针占4个字节,操作系统可以使用的最大内存空间是2^32
- 在64位系统: int类型占4个字节,指针占8个字节,操作系统可以使用的最大内存空间是2^64
- 32位系统下:int占4个字节,指针表示地址空间个数,总共有2^32个,故占4个字节
- 64位系统下:int占4个字节,指针表示地址空间个数,总共有2^64个,故占8个字节
- 指针+1等的计算,和指针类型有关。(arr+1/&arr[i])
- 整型在内存存储,指针+1是以字节为单位,还需看大小端。
- (int a=0x11223344 \ int arr[]={1,2,3,4,5}指的是数组中每个整型元素,数组的存放仍是连续)
- 一定要特别注意循环条件和数组下标从0开始和循环变量之间临界条件
下面代码的结果:
#include <stdio.h> int main() { int arr[] = {1,2,3,4,5}; short *p = (short*)arr; int i = 0; for(i=0; i<4; i++) { *(p+i) = 0; } for(i=0; i<5; i++) { printf("%d ", arr[i]); } return 0; }
//arr数组在内存中的存储格式为: 0x00ECFBF4: 01 00 00 00 0x00ECFBF8: 02 00 00 00 0x00ECFBFC: 03 00 00 00 0x00ECFC00: 04 00 00 00 0x00ECFC04: 05 00 00 00 指针p的类型为short*类型的,因此p每次只能所有两个字节,for循环对数组中内容进行修改时,一次访问的是: arr[0]的低两个字节,arr[0]的高两个字节,arr[1]的低两个字节,arr[1]的高两个字节,故改变之后,数组中内容如下: 0x00ECFBF4: 00 00 00 00 0x00ECFBF8: 00 00 00 00 0x00ECFBFC: 03 00 00 00 0x00ECFC00: 04 00 00 00 0x00ECFC04: 05 00 00 00 故最后打印:0 0 3 4 5
下面程序段的输出结果为:
unsigned long pulArray[] = {6,7,8,9,10}; unsigned long *pulPtr; pulPtr = pulArray; *(pulPtr + 3) += 3; printf("%d,%d\n",*pulPtr, *(pulPtr + 3));
unsigned long pulArray[] = {6,7,8,9,10}; unsigned long *pulPtr; pulPtr = pulArray; // 数组名代表数组首元素地址,因此pulptr指向的是数组中第一个元素的位置 *(pulPtr + 3) += 3; // pulptr+3访问的是数组中第三个元素(数组下标从0开始),故将9改为9+3=12 printf("%d,%d\n",*pulPtr, *(pulPtr + 3)); // 打印第一个和第三个元素,因此:打印6和12
在小端机器中,下面代码输出的结果是:
#include <stdio.h> int main() { int a = 0x11223344; char *pc = (char*)&a; *pc = 0; printf("%x\n", a); return 0; }
//假设,a变量的地址为0x64,则a变量在内存中的模型为: 0x64| 44 | 0x65| 33 | 0x66| 22 | 0x67| 11 | char*类型的指针变量pc指向只能指向字符类型的空间, 如果是非char类型的空间,必须要将该空间的地址强转为char*类型。 char *pc = (char*)&a; pc实际指向的是整形变量a的空间, 即pc的内容为0x64,即44,*pc=0,即将44位置中内容改为0, 修改完成之后,a中内容为:0x11223300
上面三道题的不同处就是:指针类型+1/+3等,根据指针类型来决定的。
数组中的元素是整形,一个整形元素的存放是由大小端存放
数组中的元素存放是连续。(见上图)
- 二级指针指向的空间中存储的也是一个地址
- 二级指针也是指针变量,是用来保存一级指针的地址
- 数组的地址一般用一级指针存储,或者用数组指针接收
- 二级指针是指针,二级指针的大小看系统32/64(4/8)
- 整型指针+1,向后偏移一个整形类型的大小,即4个字节(指针+1看指针类型再决定偏移量)
- 两个指针相减,指针必须指向一段连续空间,减完之后结构代表两个指针之间相差元素的个数
- 整形指针指向一个整形空间,解引用操作访问4个字节
- 指针中存储的是地址,地址可以看成一个数据,可以比较大小
- 指针数组是一个数组,是存放指针的数组,该数组的每个元素是一个指针
- 数组[ ]不写两种情况:数组必须初始化(根据初始化说明数组大小),函数传参(本质是指针)
- 指针变量是用来存放地址的变量,存储一个地址,指向同类型的一块内存空间
- 指针变量中存的有效地址可以唯一指向内存中的一块区域,一个指针变量中只能储存一个地址
- 局部指针变量不初始化就是野指针,野指针不可以正常使用
- 局部变量没有初始化里面就是随机值
- 全局变量没有初始化里面就是0
- 关于编程题_找规律
- 注意临界条件
- 注意不要是循环变量在循环体中发生变化
- 进入while循环的条件
结构体
struct student { int num; char name[32]; float score; }stu;
- 在C语言中需要自定义类型时,要用到struct关键字(struct是结构体类型的关键字)
- 在C语言中,用struct定义的结构体,定义结构体类型变量时,需要用struct student(是用户定义的结构体类型)
- 结构体中变量的名称,称之为结构体的成员(num,score)
- stu是定义的结构体类型变量,不是名称,如果想要让stu为结构体类型名称时,必须在结构体定义时添加 typedef 关键字(见结构体篇)
- 结构体访问成员的操作符有: . 操作符 ->操作符 * 操作符
- 结构体类型变量访问结构体中成员时,使用 . 操作符
- 指向结构体类型变量的指针访问结构体中成员时,使用->操作符
- 指向结构体类型变量的指针也可以通过 . 方式访问成员,只不过要先通过 * 对该指针解引用
- 结构体指针
- 结构体数组
下面程序横线处填入:
#include < stdio.h > struct S { int a; int b; }; int main( ) { struct S a, *p=&a; //struct S a; //struct S*p=&a;结构体指针 a.a = 99; printf( "%d\n", __________); return 0; }
结构体类型变量需要访问其成员时,用.运算符, 如果是指向结构体变量的指针访问时,需要用->, 或者先对指针解引用,取到指向的结构体类型的变量,再通过.访问, 特别提醒:要注意优先级 //. 的优先级高于 *
printf( "%d\n", a.a); printf( "%d\n", p->a); printf( "%d\n", (*p).a);
下面程序的输出结果是:
struct stu { int num; char name[10]; int age; }; void fun(struct stu *p) { printf("%s\n",(*p).name); return; } int main() { struct stu students[3] = {{9801,"zhang",20}, {9802,"wang",19},{9803,"zhao",18} }; fun(students + 1); return 0; }
在main函数中先定义了一个stu结构体类型的数组students, students指向结构体的起始位置, students+1 表示该数组中下标为 1 的元素, 因此fun的形参实际指向的是students数组中的第二个元素, 故打印的是wang
关于以上练习题,大家可以动手写一写。
新学期,少说话多做事。🙂🙂🙂不要忘记敲代码哦。
✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!!
代码----------→【gitee:https://gitee.com/TSQXG】
联系----------→【邮箱:2784139418@qq.com】