指针概念辨析
- 指针 - 指针得到的是指针和指针值之间的 元素个数
- 整形指针解引用访问 4个字节
- 指针可以比较大小
- 整形指针+1 意味着 向后偏移4个字节
- 当使用free释放掉一个指针内容后,指针变量的值不会被更改,需要手动置NULL
- 野指针是指向未分配或者已经释放的内存地址
- char* p = "hello";中p指向字符串第一个元素地址
- 数组指针是指针 ; 指针数组是数组
- int* fun(int a,int b) 与 (int*)fun(int a,int b)相同,都是代表函数声明
- char* arr[5] = {"hello"};test(arr);与之对应的子函数void test(char* *arr);
数组概念辨析
- 除了sizeof(arr)和&arr中的数组名,其他地方出现的数组名arr,都是数组首元素的地址
- 二维数组能省略行,不能省略列
调试常见概念
- C语言中常见的错误分类包括编译错误,连接错误,运行时错误等
- Debug被称为调试版本,不做优化;而Release被称为发布版本,不能调试
- 声明一个指向char类型变量的指针p,p值不可修改,p指向的变量值可以修改,则char*const p
代码识别
1.unsiged char的存储计算(整型提升)
#include
//unsigned char的存储计算
int main()
{
unsigned char a = 200;
unsigned char b = 100;
unsigned char c = 0;
c = a + b;
printf("%d %d",a + b,c);//输出结果为 300 44
return 0;
//解析
//char存放一个字节 1byte = 8bit a,b均为正数,原反补码均相同(在内存中以补码形式存储)
//a是无符号位unsigned定义的,即1100 1000 而因为200是个整型所以真正的比特位00000000 00000000 00000000 11001000
//b是无符号位unsigned定义的,即0110 0100 而因为100是个整型所以真正的比特位00000000 00000000 00000000 01100100
//c = a + b 要发生整型提升?
//因为两者相加一定会超过8个bit位,必然发生整型提升
//00000000 00000000 00000000 11001000 - a
//00000000 00000000 00000000 01100100 - b
//00000000 00000000 00000001 00101100 - c(unsigned char占8bit)
//因为c是由unsigned char定义的 要拿出 8个bit
//00101100 - c 即44
// 如果没有存到c里边,单单只是 a+b
//则为 00000000 00000000 00000001 00101100 - a+b
// 即为300
}
2.小端与大端存储的识别
#include
//在小端模式和大端模式的处理器分别输出多少
int main()
{
unsigned int a = 0x1234;
unsigned char b = *(unsigned char*)&a;
printf("%d",b);//小端52 大端0
return 0;
//解析
//&a取到a的地址,
//将地址强转为无符号 unsigned char*(指针)类型,*(unsigned char*)&a在对地址解引用得到地址得到内容,即1字节内容
//小端模式 低->高
//34 12 00 00
//得到十六进制34 即52
//大端模式 低->高
//00 00 12 34
//得到十六进制00 即0
}
3.数组存储空间开辟与不同指针指向同一字符
#include
int main()
{
char str1[] = "hello";
char str2[] = "hello";
char* str3 = "hello";
char* str4 = "hello";
if(str1 == str2)
printf("\n");
else
printf("str1 and str2 are not same\n");
if(str3 == str4)
printf("\n");
else
printf("str3 and str4 not point same local\n");
return 0;
//输出结果为: str1 and str2 are same
// str3 and str4 point same local
}

代码试题
1.逆序函数
#include
#include
#include
void reverse(char *arr);
//逆序函数,要将字符数组中元素逆序
//答:先写逻辑,将数组进行赋值后,将指针指向开始,和结尾,当两个指针指向同一个元素时,循环结束
int main()
{
char arr[255] = {0};
scanf("%s",arr);
reverse(arr);
printf("%s",arr);
return 0;
}
void reverse(char *arr)
{
//保证值的有效性
assert(arr);
//查找字符串的长度
int len = strlen(arr);
//数组的第一个元素地址
char* left = arr;
//数组的后边要交换的元素的位置
//arr指向数组第0个元素
//len是长度,在数组是以0开始的,所以要len-1
//arr + len - 1代表数组的最后一个元素地址
char* right = arr + len - 1;
while (left <= right)
{
int tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
2.产生类似2 22 222 2222 ...的前n项和
#include
//
//答:先写逻辑,由2 变 22 在变 222 中途以 2*10*n + 2 在不断循环演变,要求出和,则用循环将每一项都加起来
int main()
{
int n = 0;//输出2的个数
int m = 0;//输出2的个数的几次
scanf("%d%d",&n,&m);
int i = 0;
int sum = 0;
int ret = 0;//当作第几次乘以10,从零开始
for (i = 0; i < m;i++) //循环 m 次 即输出2的次数
{
//通过ret计算2 22 222 ...
ret = ret * 10 + n;
//将上边的计算结果累加到sum中取
sum = ret + sum;
printf("%d ",ret);
}
printf("\nsum :%d",sum);
}
3.水仙花数详解
//打印水仙花数(自幂数)在10万范围内
//答:先写逻辑,水仙花数是什么?水仙花数是指一个 3 位数,它的每个数位上的数字的 3次幂之和等于它本身。例如:1^3 + 5^3+ 3^3 = 153。
//通过以上定义可知,首先要知道其位数,后要求出每一位的次方(即位数),后求和。通过循环进行遍历每一个数值,得出结论
int main()
{
int i = 0;
for (i = 0;i < 100000;i++)
{
//1.求出数值的位数
int n = 1; //一个数的最小位数为 1位数
int tmp = i; //将主循环的值拿过来,进行比对,找到每个数的位数
while (tmp=tmp/10) //比如123 123/10=12 12/10=1 1/10=0 这里就是为什么我们将n定义为1的原因
{
n++;
}
//2.计算i的每一位n次方之和
tmp = i;
int sum = 0;
while (tmp)//计算每一位的当前次方的值的总和
{
sum = sum + pow(tmp%10,n); //tmp%10即每一位数值 pow(n,m)即为n的m次方
tmp = tmp / 10; //去掉 123 的个位数 ,变为12,在进入循环,以此类推
}
//3.比较是不是水仙花数的特征即 i == sum
if( i == sum)
{
printf("%d ",i);
}
}
}
4.自定义打印菱形
#include
//自定义打印菱形
// *
// ***
// *****
// ***
// *
//答:先写逻辑,第一行的*与第n行的*的开始位置相差m个位置的空格,每行空格之间差一个*,这是上三角;下三角与前相反
int main()
{
int line = 0;//定义最长及上半部分的行数
int i = 0;
scanf("%d",&line);
//打印上半部分(包含中间)
for (i = 0;i < line ;i++)
{
//1.打印空格
int j = 0;
for (j = 0;j < line- 1 - i ;j++)//line-1代表第一行的空格数 ,后-i代表遍历每行空格数(避免每行空格数量不变,同时将空格数-i个)
{
printf(" ");
}
//2.打印*
for (j = 0;j < 2 * i + 1;j++)//i从第一行开始以2i+1的数列递增
{
printf("*");
}
printf("\n");
}
//打印下部分
for (i = 0; i < line - 1; i++)//line - 1 代表不包含中间最长的一行
{
//1.打印空格
int j = 0;
for (j = 0; j <= i ; j++)//从0行开始打印,直到等于line行数
{
printf(" ");
}
//2.打印*
for (j = 0; j < 2*(line - 1- i) -1 ; j++)//((line -1 )【代表下部分的行数】*2)-1代表这一行的应有*的个数,在其中-1是避免每行的*个数打印出来的相同
{
printf("*");
}
printf("\n");
}
}
5.喝汽水
//喝汽水,两个空瓶换一瓶汽水,假设你买了20瓶,求你最后喝了几瓶
//答:先写逻辑,每次被2整除+被2取余,得到你的能换的汽水数,在此过程中,去找店主换汽水,循环往复
#include
int main()
{
int water = 0;//没喝的的水
int empty = 0;//空瓶子
int total = 0;//喝了多少汽水
int i = 0;
int j = 0;
scanf("%d",&water);
//买来的汽水全部喝掉
total = water;
empty = water;
//换汽水
while (empty>=2) //只有瓶子大于等于两个瓶子时,才可以换
{
total += empty / 2;//empty / 2 代表能换多少汽水 + total = 一共喝了多少汽水
i = empty / 2;//换汽水后喝后的空瓶数
j = empty % 2;//加上一个‘单’,除不尽时可能还剩一瓶子
empty = i + j;
}
printf("TOTAL : %d",total);//输入20时 输出 TOTAL : 39
return 0;
}
6.存储一串数组,将其数字排列,奇数在前,偶数在后
#include
void bubble_sort(int *arr, int sz);
void move(int *arr, int sz);
int main()
{
int arr[10] = {5,3,2,1,6,7,8,9,10,0};
int sz = sizeof (arr) / sizeof(arr[0]);
bubble_sort(arr,sz);
return 0;
}
void move(int *arr, int sz) {
int left = 0;
int right = sz - 1;
//循环条件
while (left < right) {
//找奇数
while ((*(arr + left) % 2 == 1) && (left < right)) {
left++;
}
//找偶数
while ((*(arr + right) % 2 == 0) && (left < right)) {
right--;
}
//交换奇偶
if (left < right) {
int tmp = *(arr + left);
*(arr + left) = *(arr + right);
*(arr + right) = tmp;
}
}
int i = 0;
for (i = 0; i < sz ; i++)
{
printf("%d ",*(arr + i));
}
printf("\n");
}
void bubble_sort(int *arr, int sz)
{
int i = 0;
//外层循环,我比到数字的位置从0开始
for (i = 0;i < sz - 1; i++)
{
//内层循环,即我得数组跟谁比
int j = 0;
//sz - 1代表整个数组得下角标 在-i的目的是在比较完这次后,这个数字我就不用比了
for (j = 0;j < sz - 1 - i;j++)
{
//这是升序排列
if(*(arr + j)>*(arr + j + 1))
{
int tmp = *(arr + j);
*(arr + j) = *(arr + j + 1);
*(arr + j + 1) = tmp;
}
}
}
for (i = 0; i < sz ; i++)
{
printf("%d ",*(arr + i));
}
printf("\n");
//将其奇偶排序
move(arr,sz);
}
7.杨辉三角(不理想版)
#include
//打印杨辉三角
// 1
// 1 1
// 1 2 1
// 1 3 3 1
// ........
//思路:先写逻辑,根据杨辉三角的特性,我可以将其变形
//1
//1 1
//1 2 1
//1 3 3 1
//得到如上这幅图
//也就是说我们可以先打印二维数组,将他的第一列和对角线全部赋值为1,而中间需要计算的元素,即每2个元素相加的出下一列中间的元素
int main()
{
//存放数字的二维数组
int arr[10][10] = {0};
int i = 0;//控制行
int j = 0;//控制列
for (i = 0; i < 10;i++)
{
for (j = 0;j <= i;j++)//保证数组它是一个下三角,草稿纸上画下就出来了
{
//第一列为1
if(j == 0)
{
arr[i][j] = 1;
}
//对角线为1,比如三行三列为1,但数组表示为[2][2]
if(j == i)
{
arr[i][j] = 1;
}
//计算数值(不包括第一列和第二行)
if(i > 1 && j > 0)//比如:2行 1列 = 1行 0列 + 1行 1列
{
arr[i][j] = arr[i-1][j-1] + arr[i-1][j];
}
}
}
for (i = 0;i < 10;i++)
{
for (j = 0; j <= i; ++j)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
8.猜凶手
#include
//猜凶手 , 已知3人说了真话,1个人说了假话
//A说:不是我
//B说:是C
//C说:是D
//D说:C再胡说
//答:先写逻辑,用已知条件,逐个判断进行假设,排除每个嫌疑
int main()
{
int killer = 0;
//从a开始判断
for (killer = 'a';killer <= 'd';killer++)
{
//满足条件即为凶手
if(((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd')) == 3)
{
printf("murderer is %c",killer);
}
}
}
9.猜名次
#include
//猜名次
//5位运动员参加了10米跳水比赛,根据以下比赛结果
//A选手说:B第二,我第三
//B选手说:我第二,E第四
//C选手说:我第一,D第二
//D选手说:C最后,我第三
//E选手说:我第四,A第一
//比赛结束后,每位选手都说答对了一半,请变成确定比赛名次
//答:先写逻辑,可以采用枚举暴力法,从全部选手所说的第一个条件,依次遍历每个选手另一个条件,直到条件满足,
//我们可以将答对赋值为1,答错赋值为0,最终结果为5;以及再加上附加条件即遍历到某个排列组合的乘积为1*2*3*4*5=120
//最终确定比赛名次
int main()
{
int a = 1;//A选手
int b = 1;//B选手
int c = 1;//C选手
int d = 1;//D选手
int e = 1;//E选手
for (a = 1; a <= 5;a++)
{
for (b = 1;b <= 5;b++)
{
for (c = 1;c <= 5;c++)
{
for (d = 1;d <= 5;d++)
{
for (e = 1;e <= 5;e++)
{
if(
((b==2)+(a==3)==1)&&
((b==2)+(e==4)==1)&&
((c==1)+(d==2)==1)&&
((c==5)+(d==3)==1)&&
((e==4)+(a==1)==1)
)
{
if(a*b*c*d*e==120){
printf("a=%d b=%d c=%d d=%d e=%d",a,b,c,d,e);
}
}
}
}
}
}
}
return 0;
}
10.左旋字符串
#include
#include
#include
void left_bl_move(char *arr, int n);
void left_fz_move(char *arr, int n);
void reverse(char *left, char *right);
//实现一个函数,可以左旋字符串中的k个字符
//例如:旋转一个ABCD左旋得到BCDA
//左旋转两个字符得到CDAB
//答:方法一:先写逻辑,暴力求解法,将传进来的值存放进一个临时变量,将后边的每一项向前移
// 方法二:先写逻辑,三步翻转法,翻转2个字符
// abcdef
// ba fedc 将逆序
// cdefab 再逆序
int main()
{
char arr[] = "abcdef";
int n = 0;
scanf("%d",&n);
left_bl_move(arr,n);//暴力求解法
left_fz_move(arr,n);//三步翻转法
}
void left_fz_move(char *arr, int n)
{
assert(arr);//检查值是否有效
int len = strlen(arr);
reverse(arr,arr+n-1);//abcdef -> ba cdef
reverse(arr+n,arr+len-1);//ba cdef -> ba fedc
reverse(arr,arr+len-1);//ba fedc ->cdefab
int i = 0;
printf("\n");
printf("%s",arr);
}
void reverse(char *left, char *right)
{
while (left < right)
{
char tmp = *left;
*left = *right;
*left = tmp;
left++;
right--;
}
}
void left_bl_move(char *arr, int n)
{
assert(arr);//检查值是否有效
int i = 0;
int len = strlen(arr);
for (i = 0;i < n;i++)
{
//将要替换i个值拿出来
char tmp = *arr;
//将这个值后变得值往前移
int j = 0;
for (j = 0;j < len-1;j++)//len-1最右边元素位置
{
//将后边元素向前移动n个位置
*(arr + j) = *(arr + j + 1);
}
//将tmp移过去
*(arr + len -1) = tmp;
}
printf("%s",arr);
}
11.判断目标字符串是否由原字符串左旋得来
#include
#include
#include
int is_left_move(char *arr1, char *arr2);
void left_move(char *arr, int n);
int is_left_move1(char *arr1, char *arr2);
//
//写一个函数,判断字符串是否为另外一个字符串旋转之后的字符串
//例如:S1 = AABCD和 S2 = BCDAA。返回1 给定s1 = abcd和s2=acbd,返回0
//答:先写逻辑,方法一:暴力法,定义两个字符串,将一个字符串左旋(遍历)与目标字符串进行比较(strcmp)
// 方法二:将主串复制两次,将目标串依次与主串左到右比较
int main()
{
//定义两个字符串,不能用char*进行赋值(不可修改)
char arr1[] = "abcdef";
char arr2[] = "cdefab";
//左旋判断
// 方法一
int ret = is_left_move(arr1,arr2);
if(ret == 1)
printf("YES");
else
printf("NO");
// 方法二
//int ret1 = is_left_move1(arr1,arr2);
// if(ret1 == 1)
// printf("YES");
// else
// printf("NO");
return 0;
}
int is_left_move1(char *arr1, char *arr2) {
//判断两个字符串长度是否相同
int len1 = strlen(arr1);
int len2 = strlen(arr2);
if(len1 != len2)
return 0;
//将主串复制两层strncat()函数 strncat(*destination,*src,size_t n);
//注意:在destination==src时,即目标串与主串进行拼接时,不能用strcat(*destination,*src);
strncat(arr1,arr1,6);
//abcdefabcdef
// cdefab
//找到相应首元素的相对位置
char* ret = strstr(arr1,arr1);
if(ret == NULL)
{
return 0;
}
else
return 1;
}
int is_left_move(char *arr1, char *arr2) {
int i = 0;
int len = strlen(arr1);
for (i = 0;i < len;i++)
{
//将源字符串进行左旋1个位置
left_move(arr1, 1);
//比较两个字符串,是否相同,相同返回0 不同:arr1 < arr2 返回值小于0
int ret = strcmp(arr1, arr2);
if (ret == 0)
return 1;
}
return 0;
}
void left_move(char *arr, int n)
{
assert(arr);//检查值是否有效
int i = 0;
int len = strlen(arr);
for (i = 0;i < n;i++)
{
//将要替换i个值拿出来
char tmp = *arr;
//将这个值后变得值往前移
int j = 0;
for (j = 0;j < len-1;j++)//len-1最右边元素位置
{
//将后边元素向前移动n个位置
*(arr + j) = *(arr + j + 1);
}
//将tmp移过去
*(arr + len -1) = tmp;
}
}
12.杨氏矩阵
#include
int query(int arr[][3], int n, int* row, int* col,int* row_r);
//杨氏矩阵
//有一个数字矩阵,矩阵每行从左向右递增,矩阵从上到下递增,
//请编写程序在这样的矩阵中查找某个数字是否存在
//要求:时间复杂度小于O(n)
//答:先写逻辑,比如:123
// 456
// 789
//如上矩阵,我们假设要查找7这个数字
//根据题目条件,可以从右上角往左下角查
//3比7小,列--,行++ ===》第一行第三列
//5比7小,列--,行++ ===》第二行第二列
//7与7等,查到数值,返回行列 ===》第三行第三列
int main()
{
int arr[][3] = {{1,2,3},{4,5,6},{7,8,9}};
//行数计算
int row = 0;
int row_r = sizeof(arr) / sizeof(arr[0]);
//列数计算
int col = sizeof(arr[0]) / sizeof(arr[0][0]) - 1;
int n = 0;
//输入所要查找的数值
scanf("%d",&n);
//做查询(传参:数组,查值,数组行数地址,数组列数地址,行数地址)
int ret = query(arr,n,&row,&col,&row_r);
if(ret == 1)
{
printf("%d %d\n", row, col);
printf("YES");
}
else
{
printf("NO");
}
}
int query(int arr[][3], int n, int* row, int* col,int* row_r)
{
//数组行数<行数,且列数>=0时,循环执行
while (*row < *row_r&&*col >= 0){
//当右上角数值小于n时,行++,列--
if (arr[*row][*col] < n) {
(*col)--;
(*row)++;
}
//当被查元素在统一行时,列--
else if (arr[*row][*col] > n) {
(*col)--;
}
//查找到元素
else if(arr[*row][*col] == n){
return 1;
}
//没找到
else{
return 0;
}
}
}