【C语言】指针由浅入深全方位详解!!!

目录

指针

野指针

二级指针 

指针数组 

字符指针 

数组指针 

数组参数,指针参数 

函数指针 

函数指针数组

回调函数 

练习题 

代码仓库 


指针

1. 指针定义

1. 指针是内存中一个最小单元的编号,也就是地址。

2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。

3. 我们可以通过 &(取地址操作符) 取出变量的内存起始地址,把地址可以存放到一个变量中,这个变量就是指针变量。

4. 指针的大小在32位平台是4个字节,在64位平台是8个字节

int a = 100;
int * pa = &a; 
//*表示pa是指针变量。
//int表示 1.pa指向的类型是int 2.pa解引用的时候访问的对象大小是sizeof(int)。

2. 指针类型

1. 指针类型可以决定指针解引用的时候访问多少个字节(指针的权限)

int* 的指针解引用访问4个字节。

char* 的指针解引用访问1个字节

.

2. 指针类型决定指针加1减1操作时的步长

整型指针+1跳过4个字节,字符指针+1跳过1个字节

3. 指针运算 

1. 指针加减整数

int arr[10] = {0};
int* p = &arr[0];
for(int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{*p = i;p = p + 1; //指针加1,加一个sizeof(int)
}p = arr;
for(int i=0; i<sizeof(arr)/sizeof(arr[0]); i++) { printf("&d ", *(p+i)); }//*(p+i) == arr[i]
//*(arr+i) == arr[i] == *(i+arr) == i[arr]

2. 指针减指针 

int arr[10] = {0};
printf("%d\n", &arr[9] - &arr[0]); //等于9
//指针减指针的绝对值是指针和指针之间的元素个数
//指针和指针相减的前提是两个指针指向了同一块空间

3. 指针的关系运算 

  • 地址是有大小的,指针的关系运算就是比较指针的大小。
  • 标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

4. 指针与数组的联系

1. 数组是一块连续的空间,可以存放1个或多个类型相同的数据。

2. 数组名是数组首元素地址,数组名==地址==指针。
3. 因为数组是连续存放的,所以通过指针就可以遍历访问整个数组。


野指针

1. 概念

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

2. 野指针成因

//1.指针未初始化
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;//2.指针越界访问int arr[10] = {0};int *p = arr;int i = 0;for(i=0; i<=11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}//3.指针指向的空间释放

3. 如何规避野指针? 

1.明确知道指针应该初始化为谁的地址就直接初始化,不知道的就初始化为NULL。

2.小心指针越界。

3.指针指向的空间释放后,及时置NULL。

4.避免返回局部变量的地址。

5.指针使用前检查有效性。


二级指针 

1. 指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?

pp是二级指针变量,用来存放一级指针变量的地址

int a = 10;
int* p = &a; 
int** pp = &p; 

p是指针变量,是变量就有地址

pp存放p的地址


指针数组 

1. 类比

整型数组 - 存放整型的数组
字符数组 - 存放字符的数组
指针数组 - 存放指针(地址)的数组

.

int* arr1[10]; //存放整形指针的数组

char* arr2[4]; //存放字符指针的数组

char** arr3[5];//存放二级字符指针的数组

2. 使用指针数组模拟二维数组

真正的二维数组是连续存放的


字符指针 

1. char* 的两种使用方式

//在指针的类型中我们知道有一种指针类型为字符指针char*
int main()
{char ch = 'w';char *pc = &ch;*pc = 'w';return 0;
}//还有一种使用方式如下:
int main()
{//这里本质是把字符串hello bit.首字符的地址放到了pstr中const char* pstr = "hello bit."; printf("%s\n", pstr);return 0;
}

2. 面试题

//输出结果?#include <stdio.h>int main(){char str1[] = "hello bit.";char str2[] = "hello bit.";const char *str3 = "hello bit.";const char *str4 = "hello bit.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;}

str1和str2不同,原因如下

1. str1和str2是两个不同的数组意味着不同的空间

2. str1, str2是数组名也就是首元素地址,空间不同地址自然不同

.

str3和str4相同,原因如下

1. "hello bit." 是一个常量字符串在内存的常量区,然后首元素地址给str3

2. 因为常量字符串无法改变,所以它可以再次将首元素地址给str4

3. 这里str3和str4指向的是一个同一个常量字符串。

.

C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。


数组指针 

1. 类比

整型指针 - 指向整型变量的指针,存放整型变量的地址的指针变量

字符指针 - 指向字符变量的指针,存放字符变量的地址的指针变量

数组指针 - 指向数组的指针,存放数组的地址的指针变量

2. 指针数组与数组指针 

p1, p2分别是什么?

int* p1[10];

int (*p2)[10];

解释:p1是指针数组,p2是数组指针。

p2先和 * 结合,说明p2是一个指针变量,然后指针指向的是一个大小为10个整型的数组。

所以p2是一个指针,指向一个数组,叫数组指针。

这里要注意:[ ]的优先级要高于 * 号的,所以必须加上()来保证p2先和 * 结合。

3. &数组名 与 数组名 

&arr与arr,输出的值是一样的

但是类型不一样

实际上:&arr表示的是整个数组的地址,而不是数组首元素的地址。

本例中&arr 的类型是:int(*)[10],是一种数组指针类型。

.

数组的地址+1,跳过整个数组的大小,所以&arr+1相对于&arr的差值是40。

.

数组地址的存放

3. 数组指针一般用在二维数组传参

void print_arr2(int (*arr)[5], int row, int col){int i = 0;for(i=0; i<row; i++){for(j=0; j<col; j++){printf("%d ", arr[i][j]);}printf("\n");}}int main(){int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};print_arr2(arr, 3, 5);return 0;}

print_arr2(arr, 3, 5);
1. 数组名arr,表示首元素的地址。
2. 但是二维数组的首元素是二维数组的第一行。
3. 所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址,需要一个数组指针来接收。


数组参数,指针参数 

1. 一维数组传参

例子1

void test(int arr[]){}//这个没问题
void test(int arr[10]){}//这个也没问题,可见对[]内的数字没有要求
void test(int *arr){}//传入数组名是首元素地址,用指针接收也没问题int main()
{int arr[10] = {0}; //一维整型数组test(arr);
}

例子2

void test2(int *arr[20]){} //这个没问题
void test2(int **arr){} //传入数组名是首元素地址也就是一个一级指针的地址,所以用二级指针接收int main()
{int *arr2[20] = {0}; //一维指针数组test2(arr2);
}

2. 二维数组传参  

例子1

void test(int arr[3][5]){} //正确
void test(int arr[][]){} //错误
void test(int arr[][5]){} //正确
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。void test(int *arr){} //错误
void test(int* arr[5]){} //错误
void test(int (*arr)[5]){} //正确
void test(int **arr){} //错误
//传入了一个整型一维数组的地址,需要一个类型为整型一维数组的指针接收int main()
{int arr[3][5] = {0};test(arr); //传入数组名也就是首元素地址,二维数组首元素地址是一个一维数组的地址
}

3. 一级指针传参

例子1

void print(int *p, int sz)
{int i = 0;for(i=0; i<sz; i++) printf("%d\n", *(p+i));
}int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9};int *p = arr;int sz = sizeof(arr)/sizeof(arr[0]);print(p, sz); //一级指针变量p,传给函数return 0;
}

思考:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

4.  二级指针传参

例子1

void test(int** ptr) printf("num = %d\n", **ptr);    int main()
{int n = 10;int*p = &n;int **pp = &p;test(pp); //pp是二级指针变量test(&p); //&p是一级指针地址return 0;
}

思考: 当函数的参数为二级指针的时候,可以接收什么参数?


函数指针 

1. 类比

数组指针 - 指向数组的指针

函数指针 - 指向函数的指针

2. 函数名 和 &函数名一样,都是函数的地址 

函数地址的存放

int (*pf)(int, int) = &Add; //pf是函数指针变量,前面返回值,后面参数
int (*)(int, int) //去掉名字是函数指针类型

3. 函数指针变量调用函数

int Add(int x, int y){return x+y;}int main()
{//int (*pf)(int, int) = &Add;int (*pf)(int, int) = Add; //这两个写法一样int a = Add(3, 5);int b = (*pf)(3, 5);int c = pf(3, 5); //这三个一样,可以不写 *return 0;
}

4. 有趣的代码 

//代码1
(*(void(*)())0)();
//1. void(*)()是无返回值无参数的函数指针类型
//2. *(void(*)())0 对0进行强制转换,然后解引用
//3. (*(void(*)())0)() 调用0地址处的这个函数//代码2
void (*signal(int, void(*)(int)))(int);
//1. signal(int, void(*)(int)) 这是一个函数,参数是整型和函数指针类型
//2. void (*)(int) 把刚刚的函数去掉剩下就是返回类型
//3. 所以这是一个函数声明代码2可读性不强,我们可以用typedef
typedef void (*pf_t)(int); //将void (*)(int) 重命名为pf_t
void (*signal(int, void(*)(int)))(int); //原代码
pf_t signal(int, pf_t); //重命名后

函数指针数组

1. 类比

int* arr[5] //整型指针数组

char* arr[5] //字符指针数组

函数指针数组:数组的每个元素都是函数指针类型

2. 用函数指针实现计算器 

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }void menu() { printf("0.exit, 1.add, 2.sub, 3.mul, 4.div\n"); }int main()
{int (*pf_arr[])(int, int) = { NULL, add, sub, mul, div }; //函数指针数组,转移表int input, a, b;int sz = sizeof pf_arr / sizeof pf_arr[0];while (1){menu();scanf("%d", &input);if (input == 0) break;else if (input > 0 && input <= sz){scanf("%d %d", &a, &b);printf("%d\n", pf_arr[input](a, b));}else printf("input err\n");}return 0;
}

3. 指向函数指针数组的指针 

指向函数指针数组的指针是一个指针,这个指针指向一个数组,这个数组的元素都是函数指针。 如何定义?

int (*pf)(int, int); //函数指针
int (*pf_arr[4])(int, int); //函数指针数组
int (*(*p_pf_arr)[4])(int, int) = &pf_arr; //函数指针数组的地址
//p_pf_arr就是指向函数指针数组的指针

回调函数 

1. 什么是回调函数

回调函数就是一个通过函数指针调用的函数。

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

2. 利用回调函数实现计算器 

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }void menu() { printf("0.exit, 1.add, 2.sub, 3.mul, 4.div\n"); }int Cal(int (*pf)(int, int))
{int a, b;scanf("%d %d", &a, &b);printf("%d\n", pf(a, b));
}int main()
{int input = 1;while (input){menu();scanf("%d", &input);switch (input){case 0:break;case 1:Cal(add);break;case 2:Cal(sub);break;case 3:Cal(mul);break;case 4:Cal(div);break;}}return 0;
}

3. qsort函数

qsort函数特点:

1. 采用快速排序的方法

2. 适合任意类型数据的排序

.

qsort函数参数

void qsort(void* base, //指向需要排序的数组第一个元素

                 size_t num, //排序个数

                 size_t size, //一个元素的大小

                 int (*cmp)(const void*, const void*) //函数指针类型,指向的函数能比较base中元素

                );

.

qsort使用

//void*的指针可以接收任意类型的地址
//但是不能直接解引用和指针运算
//需要强制转换成对应类型
int cmp_int(const void* v1, const void* v2) { return *(int*)v1 - *(int*)v2; }int main()
{int arr[] = { 2, 8, 9, 6, 4, 5, 1 };int sz = sizeof arr / sizeof arr[0];qsort(arr, sz, sizeof arr[0], cmp_int);return 0;
}

4. 模拟实现qsort函数

冒泡排序思想:两两相邻的元素比较,不满足条件就交换

	//冒泡排序
int main()
{int arr[] = { 2, 8, 9, 6, 4, 5, 1 };int sz = sizeof arr / sizeof arr[0];int tmp;//需要比较sz-1趟for (int i = 0; i < sz - 1; i++){//每一趟完成后少一个数 for (int j = 0; j<sz-1-i; j++){if (arr[j] > arr[j + 1]){tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}return 0;
}

利用冒泡排序的思想模拟实现qsort函数

问题1:第一个参数如何接收不同类型。

解决1:void*的指针解决并给出个数和每个的大小

问题2:不同类型元素比较方式可能不同

解决2:将两个元素的比较方法作为函数参数传递

问题3:不同的数据类型进行交换时不一样

解决3:利用char*一次只交换一个字节,循环目标字节大小次即可

//比较
int cmp_int(const void* v1, const void* v2) { return *(int*)v1 - *(int*)v2; }
//交换
void swap(char* p1, char* p2, int sz)
{char tmp;for (int i = 0; i < sz; i++){tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}
//排序
void bubble_sort(void* base, int num, int sz, int (*cmp)(const void*, const void*))
{//冒泡思想for (int i = 0; i < num - 1; i++){for (int j = 0; j < num - 1 - i; j++){//不同类型比较方式不一样所以不能写死if (cmp((char*)base + j * sz, (char*)base + (j + 1) * sz) > 0){//不同类型交换方式不一样所以不能写死swap((char*)base + j * sz, (char*)base + (j + 1) * sz, sz);}}}
}int main()
{int arr[] = { 2, 8, 9, 6, 4, 5, 1 };int num = sizeof arr / sizeof arr[0];bubble_sort(arr, num, sizeof arr[0], cmp_int);return 0;
}

练习题 

数组名表示数组首元素的地址,
但有两个例外:
1. sizeof(数组名),这里的数组名表示整个数组,求整个数组的大小
2. &数组名,这里的数组名表示整个数组

输出结果?(默认地址为4字节)

int a[] = {1,2,3,4};printf("%d\n",sizeof(a)); //整个数组printf("%d\n",sizeof(a+0)); //首元素地址加0printf("%d\n",sizeof(*a)); //首元素地址解引用printf("%d\n",sizeof(a+1)); //首元素地址加1printf("%d\n",sizeof(a[1])); //第二个元素printf("%d\n",sizeof(&a)); //整个数组的地址printf("%d\n",sizeof(*&a)); //整个数组的地址解引用printf("%d\n",sizeof(&a+1)); //整个数组的地址加1printf("%d\n",sizeof(&a[0])); //第一个元素的地址printf("%d\n",sizeof(&a[0]+1)); //第一个元素的地址加1

答:16 4 4 4 4 4 16 4 4 4

 输出结果?(默认地址为4字节)

char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr)); //整个数组printf("%d\n", sizeof(arr+0)); //首元素地址加0printf("%d\n", sizeof(*arr)); //首元素地址解引用printf("%d\n", sizeof(arr[1])); //第二个元素printf("%d\n", sizeof(&arr)); //数组的地址printf("%d\n", sizeof(&arr+1)); //数组地址加1printf("%d\n", sizeof(&arr[0]+1)); //首元素地址加1//strlen统计\0之前的字符个数printf("%d\n", strlen(arr)); //随机值,无法确定\0printf("%d\n", strlen(arr+0)); //和上一个一样
//strlen需要传入一个地址,当'a'传进去会转成ASCII码值进行地址访问printf("%d\n", strlen(*arr)); //异常访问printf("%d\n", strlen(arr[1])); //异常访问printf("%d\n", strlen(&arr)); //随机值printf("%d\n", strlen(&arr+1)); //随机值printf("%d\n", strlen(&arr[0]+1)); //随机值

答:6 4 1 1 4 4 4

 输出结果?(地址默认为4)

char arr[] = "abcdef";printf("%d\n", sizeof(arr)); //整个数组大小,字符串后面还有'\0'printf("%d\n", sizeof(arr+0)); //首元素地址printf("%d\n", sizeof(*arr)); //首元素printf("%d\n", sizeof(arr[1])); //第二个元素printf("%d\n", sizeof(&arr)); //数组地址printf("%d\n", sizeof(&arr+1)); //数组地址加1printf("%d\n", sizeof(&arr[0]+1)); //第一个元素地址加1printf("%d\n", strlen(arr)); //从首元素开始到'\0'之前printf("%d\n", strlen(arr+0)); //和上一个一样printf("%d\n", strlen(*arr)); //异常访问printf("%d\n", strlen(arr[1])); //异常访问printf("%d\n", strlen(&arr)); //从首元素开始到'\0'之前printf("%d\n", strlen(&arr+1)); //随机值,因为把'\0'跳过了printf("%d\n", strlen(&arr[0]+1)); //从第二个元素开始到'\0'之前

答:7 4 1 1 4 4 4

6 6 异常 异常 6 随机 5

 输出结果?(地址默认为4)

 //这里本质是把字符串"abcdef"首字符的地址放到了p中
char *p = "abcdef";printf("%d\n", sizeof(p)); //首字符地址printf("%d\n", sizeof(p+1)); //第二个字符的地址printf("%d\n", sizeof(*p)); //首字符printf("%d\n", sizeof(p[0])); //首字符printf("%d\n", sizeof(&p)); //p的地址printf("%d\n", sizeof(&p+1)); //p的地址加1printf("%d\n", sizeof(&p[0]+1)); //首字符地址加1printf("%d\n", strlen(p)); //从首字符开始到'\0'printf("%d\n", strlen(p+1)); //从第二个字符开始到'\0'printf("%d\n", strlen(*p)); //异常访问printf("%d\n", strlen(p[0])); //异常访问printf("%d\n", strlen(&p)); //随机值printf("%d\n", strlen(&p+1)); //随机值printf("%d\n", strlen(&p[0]+1)); //从第二个字符开始到'\0'

答:4 4 1 1 4 4 4

6 5 异常 异常 随机 随机 5

 输出结果?(地址默认为4)

int a[3][4] = {0};printf("%d\n",sizeof(a)); //整个数组大小printf("%d\n",sizeof(a[0][0])); //第一个元素printf("%d\n",sizeof(a[0])); //a[0]相当于第一行这个一维数组的数组名printf("%d\n",sizeof(a[0]+1)); //第一行这个一维数组的首元素地址加1printf("%d\n",sizeof(*(a[0]+1))); //第一行的第二个元素printf("%d\n",sizeof(a+1)); //第二行的地址printf("%d\n",sizeof(*(a+1))); //第二行的首元素地址printf("%d\n",sizeof(&a[0]+1)); //第一行的地址加1printf("%d\n",sizeof(*(&a[0]+1))); //第二行的地址解引用printf("%d\n",sizeof(*a)); //二维数组首元素地址解引用printf("%d\n",sizeof(a[3])); //越界了,但sizeof只看它类型就得出结果了

答:48 4 16 4 4 4 16 4 16 16 16

解析:数组名先看是否满足两个例外之一,不满足才是首元素地址。

程序的结果是什么?

int main(){int a[5] = { 1, 2, 3, 4, 5 };int *ptr = (int *)(&a + 1);printf( "%d,%d", *(a + 1), *(ptr - 1));return 0;}

答:2,5

假设p 的值为0x100000。 如下表表达式的值分别为多少?

已知,结构体Test类型的变量大小是20个字节

struct Test{int Num;char *pcName;short sDate;char cha[2];short sBa[4];}*p = (struct Test*)0x100000;int main(){printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1); //注意这里不是指针printf("%p\n", (unsigned int*)p + 0x1);return 0;}

答:00100014 00100001 00100004

解析:第二个转成无符号长整形加1就是单纯的加1

 代码结果?

int main(){int a[4] = { 1, 2, 3, 4 };int *ptr1 = (int *)(&a + 1);int *ptr2 = (int *)((int)a + 1);printf( "%x,%x", ptr1[-1], *ptr2);return 0;}

答:4,2000000

代码结果?

#include <stdio.h>int main(){int a[3][2] = { (0, 1), (2, 3), (4, 5) };int *p;p = a[0];printf( "%d", p[0]);return 0;}

答:1

解析:二维数组对于行的初始化需要大括号,图中只是逗号表达式

代码结果?

int main(){int a[5][5];int(*p)[4];p = a;printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0;}

答:fffffffc,-4

解析:%p -- 内存是什么值就打印什么值,用十六进制

代码结果?

int main(){int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int *ptr1 = (int *)(&aa + 1);int *ptr2 = (int *)(*(aa + 1));printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));return 0;}

 答:10,5

代码结果?

int main(){char *a[] = {"work","at","alibaba"}; //放的是字符串首元素地址char**pa = a;pa++;printf("%s\n", *pa);return 0;}

答:at

解析:a是首元素地址,所以pa也是首元素地址,pa++,*pa就是第二个元素

代码结果?

int main(){char *c[] = {"ENTER","NEW","POINT","FIRST"};char**cp[] = {c+3,c+2,c+1,c};char***cpp = cp;printf("%s\n", **++cpp);printf("%s\n", *--*++cpp+3);printf("%s\n", *cpp[-2]+3);printf("%s\n", cpp[-1][-1]+1);return 0;}

答:POINT ER ST EW

答:C

解析:A有点模糊,口头语上指针是指针变量 

答:C

解析:如果指针是4个字节的话,意味着地址是由32位组成,则可以形成2的32次方个地址,就可以管理2的32次方字节的空间

 代码运行结果?

答:0 0 3 4 5

 

答:C

解析:指针比较大小就是地址比较大小

 在小端机器中,代码运行结果?

答:11223300

解析:%x 按十六进制打印

 代码运行结果?

答:6 ,12

 

答:A

解析:D是数组指针

 

答:B

解析:点操作符优先级比星号高

 代码运行结果?

答:wang

  

答:D

解析:stu是通过前面的结构体类型创建的变量

 

答:D

 

答:B

 

答:A

解析:free释放之后只是把指向的空间回收了,指针变量需要手动置空。

 

答:C

   

答:C

解析:数组指针 - 指向数组的指针

指针数组 - 存放指针的数组

答:C

解析:看变量名和谁结合,[ ]优先级比*高。

答:C

答:A

解析:把变量名去掉就能得到返回类型,这里因为函数指针返回函数指针,所以需要把函数指针整体当作变量名去掉。

答:B

解析:

答:D

解析:&arr表示整个数组的地址,类型是int (*)[10]。

 

答:A

解析:arr表示数组首元素地址,&arr表示整个数组的地址。

答:C

解析:首先这是一个数组,其次元素类型是int*。

答:B

解析:1. 返回值和参数类型需要和原函数相同。2. &fun和fun等价都是函数的地址。

 

答:C

解析:A,p和[ ]先结合所以是数组不是指针。

           B,p先和( )结合变成函数。

答: D

解析:回调函数是调用函数指针指向的函数。

 

答:BD

解析:这是一个存放字符指针的数组,字符串存放的是首字符的地址。

 答:A

解析:&aa是整个数组的地址,aa是第一行的地址,*aa是第一行的数组名也就是第一行的首元素地址。

 

答:C

解析:首先这是一个二维数组,传入首元素地址是第一行的地址。

 

思路:s1是AABCD,那么我们在后面追一个s1就得到AABCDAABCD,这个字符串就包含了s1所有旋转的可能性,再用strstr找。

int check(char* s1, char* s2)
{//在s1后面追加s1int len1 = strlen(s1);strncat(s1, s1, len1);//同时要保证原本的s1和s2长度相等int len2 = strlen(s2);if (len1 != len2) return 0;//在加长后的s1里面找是否有s2if (strstr(s1, s2) == NULL) return 0;else return 1;
}int main()
{char s1[20] = "abcd";char s2[] = "cdab";if (check(s1, s2)) printf("是\n");else printf("不是\n");return 0;
}

 

思路:以k为分界,左边逆序,右边逆序,整体逆序。

void reverse(char* left, char* right)
{while (left < right){char tmp = *left;*left = *right;*right = tmp;left++;right--;}
}void StringLeftHanded(char* str, int k)
{int len = strlen(str);k %= len;//1. 左边逆序reverse(str, str + k - 1);//2. 右边逆序reverse(str + k, str + len - 1);//3. 整体逆序reverse(str, str + len - 1);
}int main()
{char str[5] = "abcd"; StringLeftHanded(str, 2);return 0;
}

 

思路:

#define ROW 3
#define COL 3void Yang(int (*arr)[COL], int *row, int *col, int key)
{while (*col>=0 && *row<ROW){//我比这一行最大的都大if (key > arr[*row][*col]) (*row)++;//我比这一列最小的都小else if (key < arr[*row][*col]) (*col)--;//相等else return;}//走出来意味着没相等*row = *col = -1;return;
}int main()
{int arr[ROW][COL] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int key;scanf("%d", &key);int row = 0;int col = COL-1;Yang(arr, &row, &col, key);if (row == -1) printf("不存在\n");else printf("存在,下标为(%d,%d)\n", row, col);return 0;
}

//思路:要换到一直没有空瓶
int main()
{//输入金额,1金额等于1瓶int n;scanf("%d", &n);//判断喝多少瓶int sum = 0, flag = 0;while (n){sum += n;if (n % 2 == 0) n /= 2; //两空瓶等于1瓶else{flag += n % 2; //不能整除先把余数存起来再除2n /= 2;}if (flag >= 2) //余出来的空瓶超过两瓶就可以换了{flag -= 2;sum++;}}printf("%d\n", sum);return 0;
}

 

void print(int* _arr, int _sz) { for(int i=0; i<_sz; i++) printf("%d ", *(_arr+i)); }int main()
{int arr[] = { 1, 2, 3, 4 };int sz = sizeof(arr) / sizeof(arr[0]);print(arr, sz);return 0;
}

 

//思路:分上下两部分
int main()
{//输入上部分的行int line;scanf("%d", &line);//打印上部分行for(int i=0; i<line; i++){//打印空格for(int j=0; j<line-1-i; j++) printf(" ");//打印星for(int j=0; j<i*2+1; j++) printf("*");printf("\n");}//打印下部分行for(int i=1; i<line; i++){//打印空格for(int j=0; j<i; j++) printf(" ");//打印星for(int k=(line*2-1)-(2*i); k>0; k--) printf("*");printf("\n");}return 0;
}

 

int get(int num, int count)
{if (num == 0) return 0;int a = num % 10;num /= 10;return pow(a, count) + get(num, count); //pow求次方
}int main()
{for (int i = 0; i <= 100000; i++){//求i的位数int cot = 1;for (int data = i; data > 9; data /= 10) cot++; //不能在循环内改变i//判断if(get(i, cot) == i) printf("%d ", i);}return 0;
}

 

//前面一项乘10加2等于后面一项
int main()
{//输入目标数字和项数int a, n;scanf("%d %d", &a, &n);//每项求和int sum = 0, k = 0;for (int i = 0; i < n; i++){k = k * 10 + a; sum += k;}printf("%d\n", sum);return 0;
}

代码仓库 

Pointer/Pointer/main.c · 林宇恒/code_c - 码云 - 开源中国 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/389315.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】如何巧妙运用C++命名空间:初学者必备指南

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01; 本篇将带领大家走进C的旅途&#xff0c;为了更好地学习C这门语言&#xff0c;我们需要了解它的前世今生。在了解完C如何诞生后&#xff0c;将开始我们C之旅第一站"命名空间"。(老早说是C/C博主&…

Java 集合框架:HashMap 的介绍、使用、原理与源码解析

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 020 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

零基础入门转录组数据分析——GO+KEGG富集分析

零基础入门转录组数据分析——GOKEGG富集分析 目录 零基础入门转录组数据分析——GOKEGG富集分析1. 富集分析基础知识2. GO富集分析&#xff08;Rstudio&#xff09;——代码实操3. KEGG富集分析&#xff08;Rstudio&#xff09;——代码实操注&#xff1a;配套资源只要改个路径…

PyQt5| 界面设计 |利用Qt Designer实现简单界面交互

目录 1 QtDesigner简单界面设计2 代码部分2.1 ui文件转py文件2.2 界面文件代码2.3 主文件代码2.3.1 主体框架代码2.3.2 实现交互代码 3结果展示 准备工作&#xff1a; 配置好PyQt5相关的库、QtDesigner、pyuic 1 QtDesigner简单界面设计 点击“工具"——>“外部工具&a…

Matlab实现最小二乘法的几种方法

最小二乘法&#xff08;又称最小平方法&#xff09;是一种数学优化技术。它通过最小化误差的平方和寻找数据的最佳函数匹配。 按照图中所提出的问题&#xff08;如图1&#xff09;&#xff0c;要求已知多组解&#xff08;自变量和因变量&#xff09;&#xff0c;求出最佳和最恰…

【C++/STL深度剖析】priority_queue 最全解析(什么是priority_queue? priority_queue的常用接口有哪些?)

目录 一、前言 二、如何区分【优先级队列】与【队列】&#xff1f; 三、priority_queue的介绍 四、priority_queue 的构造 五、priority_queue 的常用接口 &#x1f4a7;push &#x1f4a7;pop &#x1f4a7;size &#x1f4a7;top &#x1f4a7;empty &…

C语言贪吃蛇课程设计实验报告(包含贪吃蛇项目源码)

文末有贪吃蛇代码全览,代码有十分细致的注释!!!文末有贪吃蛇代码全览,代码有十分细致的注释!!!文末有贪吃蛇代码全览,代码有十分细致的注释!!! 码文不易&#xff0c;给个免费的小星星和免费的赞吧&#xff0c;关注也行呀(⑅•͈ᴗ•͈).:*♡ 不要白嫖哇(⁍̥̥̥᷄д⁍̥̥…

【C++/STL】:vector容器的底层剖析迭代器失效隐藏的浅拷贝

目录 &#x1f4a1;前言一&#xff0c;构造函数1 . 强制编译器生成默认构造2 . 拷贝构造3. 用迭代器区间初始化4. 用n个val值构造5. initializer_list 的构造 二&#xff0c;析构函数三&#xff0c;关于迭代器四&#xff0c;有关数据个数与容量五&#xff0c;交换函数swap六&am…

SpringBoot整合Flink CDC实时同步postgresql变更数据,基于WAL日志

SpringBoot整合Flink CDC实时同步postgresql变更数据&#xff0c;基于WAL日志 一、前言二、技术介绍&#xff08;Flink CDC&#xff09;1、Flink CDC2、Postgres CDC 三、准备工作四、代码示例五、总结 一、前言 在工作中经常会遇到要实时获取数据库&#xff08;postgresql、m…

为何重视文件加密?用哪款加密软件好呢?

一、公司都重视文件加密的原因有哪些&#xff1f;保护数据安全&#xff1a;在数字化时代&#xff0c;数据是企业重要的资产之一。文件加密可以确保数据在存储和传输过程中不被未经授权的人员访问或窃取&#xff0c;从而保护数据的机密性和完整性。这对于包含敏感信息&#xff0…

Reat hook开源库推荐

Channelwill Hooks 安装 npm i channelwill/hooks # or yarn add channelwill/hooks # or pnpm add channelwill/hooksAPI 文档 工具 Hooks useArrayComparison: 比较两个数组的变化。useCommunication: 处理组件之间的通信。useCurrencyConverter: 货币转换工具。useCurre…

【Docomo】5G

我们想向您介绍第五代移动通信系统“5G”。 5G 什么是5G&#xff1f;支持5G的技术什么是 5G SA&#xff08;独立&#xff09;&#xff1f;实现高速率、大容量的5G新频段Docomo的“瞬时5G”使用三个宽广的新频段 什么是5G&#xff1f; 5G&#xff08;第五代移动通信系统&#x…

【Elasticsearch】Elasticsearch的分片和副本机制

文章目录 &#x1f4d1;前言一、分片&#xff08;Shard&#xff09;1.1 分片的定义1.2 分片的重要性1.3 分片的类型1.4 分片的分配 二、副本&#xff08;Replica&#xff09;2.1 副本的定义2.2 副本的重要性2.3 副本的分配 三、分片和副本的机制3.1 分片的创建和分配3.2 数据写…

Github Benefits 学生认证/学生包 新版申请指南

本教程适用于2024年之后的Github学生认证申请&#xff0c;因为现在的认证流程改变了很多&#xff0c;所以重新进行了总结这方面的指南。 目录 验证教育邮箱修改个人资料制作认证文件图片转换Base64提交验证 验证教育邮箱 进入Email settings&#xff0c;找到Add email address…

【一图学技术】5.OSI模型和TCP/IP模型关系图解及应用场景

OSI模型和TCP/IP模型关系图解 OSI模型和TCP/IP模型都是网络通信的参考模型&#xff0c;用于描述网络协议的层次结构和功能。下面是它们的定义和区别&#xff1a; OSI模型&#xff08;Open Systems Interconnection Model&#xff09; OSI模型是一个理论上的七层模型&#xff…

揭秘线性代数秩的奥秘:从理论到机器学习的跨越

一、线性代数中的秩&#xff1a;定义与性质 1.1 定义 在线性代数中&#xff0c;秩是一个核心概念&#xff0c;用于描述矩阵或向量组的复杂性和独立性。具体而言&#xff0c;一个矩阵的秩定义为该矩阵中非零子式的最高阶数&#xff0c;而一个向量组的秩则是其最大无关组所含的…

双 Token 三验证解决方案

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 问题分析 以往的项目大部分解决方案为单 token&#xff1a; 用户登录后&#xff0c;服务端颁发 jwt 令牌作为 token 返回每次请求&#xff0c;前端携带 token 访问&#xff0c;服务端解析 token 进行校验和…

Ubuntu配置项目环境

目录 一、Xshell连接云服务器 二、切换到root用户 三、安装jdk 四、安装tomcat 五、安装mysql 1、安装mysql服务器 2、卸载mysql服务器 六、正式进行程序的部署 一、Xshell连接云服务器 要想使用xshell连接上云服务器就需要明确云服务器的几个信息&#xff1a; 1&…

科研绘图系列:R语言GWAS曼哈顿图(Manhattan plot)

介绍 曼哈顿图(Manhattan Plot)是一种常用于展示全基因组关联研究(Genome-Wide Association Study, GWAS)结果的图形。GWAS是一种研究方法,用于识别整个基因组中与特定疾病或性状相关的遗传变异。 特点: 染色体表示:曼哈顿图通常将每个染色体表示为一个水平条,染色体…

tarojs项目启动篇

TaroJS 是一个开放式跨端开发解决方案&#xff0c;使用 React 语法规范来开发多端应用&#xff08;包括小程序、H5、React Native 等&#xff09;。它可以帮助开发者高效地构建出在不同端上运行一致的应用。以下是启动 TaroJS 项目&#xff08;本来就有的旧项目&#xff09;的步…