int main()
{
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
//二维数组的数组名,也表示首元素的地址
//二维数组的首元素是第一行
//首元素的地址就是第一行的地址,是一个一维数组的地址
print(arr, 3, 5);
return 0;
}
void test(int arr[3][5]){}//ok? YES
void test(int arr[][]){}//ok? NO
void test(int arr[][5]){}//ok? YES
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr){}//ok? NO
void test(int* arr[5]){}//ok? NO
void test(int (*arr)[5]){}//ok? YES
void test(int **arr){}//ok? NO
int main()
{
int arr[3][5] = {0};
test(arr);
}
int Add(int x, int y)
{
return x + y;
}
//&函数名得到就是函数的地址
int main()
{
//printf("%p\n", &Add);
//printf("%p\n", Add);
//pf就是函数指针
int (* pf)(int, int) = Add;//函数的地址要存起来,就得放在【函数指针变量】中
int ret = (*pf)(3, 5);
//int ret = Add(3, 5);
//int ret = pf(3, 5);
printf("%d\n", ret);
return 0;
}
int main()
{
//代码1
//1. 将0强制类型转换为void (*)() 类型的函数指针
//2. 这就意味着0地址处放着一个函数,函数没参数,返回类型是void
//3. 调用0地址处的这个函数
//下面代码是一次函数调用
//(*( void (*)() ) 0)();
//代码2
void (* signal(int, void(*)(int) ) )(int);
//
pf_t signal(int, pf_t);
//上述的代码是一个函数的声明
//函数的名字是signal
//signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
//该函数指针指向的函数参数是int,返回类型是void
//
//signal函数的返回类型也是一个函数指针 //该函数指针指向的函数参数是int,返回类型是void
//
//void (* signal(int, void(*)(int)))(int)
return 0;
}
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
模拟实现类似qsort这个功能的冒泡排序bubble_sort()
#include <stdlib.h>
#include <string.h>
//qsort函数的使用者提供这个函数
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
test1()
{
int arr[] = { 3,1,5,2,4,9,8,6,5,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
//使用qsort来排序整型数组,这里就要提供一个比较函数,这个比较函数能够比较2个整数的大小
//qsort 默认是排成升序的
qsort(arr, sz, sizeof(arr[0]), cmp_int);
print_arr(arr, sz);
}
//测试qsort 排序结构体数据
struct Stu
{
char name[20];
int age;
};
//按照年龄来比较
int cmp_stu_by_age(const void* p1, const void* p2)
{
return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
int cmp_stu_by_name(const void* p1, const void* p2)
{
return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
void test2()
{
struct Stu s[] = { {"zhangsan", 30}, {"lisi", 25}, {"wangwu", 50}};
int sz = sizeof(s) / sizeof(s[0]);
//测试按照年龄来排序
//qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
//测试按照名字来排序
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
void bubble_sort(void* base, size_t num, size_t width,
int (*cmp)(const void* p1, const void* p2))
{
//要确定趟数
size_t i = 0;
for (i = 0; i < num - 1; i++)
{
int flag = 1;//假设已经有序了
//一趟冒泡排序的过程
size_t j = 0;
for (j = 0; j < num - 1 - i; j++)
{
//两个相邻的元素比较
//arr[j] arr[j+1]
if (cmp((char*)base+j*width, (char*)base+(j + 1)*width)>0)
{
//交换
flag = 0;
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
if (flag == 1)
{
break;
}
}
}
void test3()
{
int arr[] = { 3,1,5,2,4,9,8,6,5,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
print_arr(arr, sz);
}
void test4()
{
struct Stu s[] = { {"zhangsan", 30}, {"lisi", 25}, {"wangwu", 50} };
int sz = sizeof(s) / sizeof(s[0]);
//测试按照年龄来排序
//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
//测试按照名字来排序
bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
test1();
test2();
test3();
test4();
return 0;
}
彻底搞懂strlen和sizeof的一些试题(附加答案)
数组和指针
数组 - 能够存放一组相同类型的元素,数组的大小取决于数组的元素个数和元素类型
指针 - 地址/指针变量 ,大小是4/8个字节
数组是数组,指针是指针,二者不等价
数组名是数组首元素的地址,这个地址就可以存放在指针变量中
我们就可以使用指针来遍历数组
数组名
大部分情况下数组名是数组首元素的地址
但是有2个例外:
sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小
&数组名 - 数组名表示整个数组,取出的是数组的地址
//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//16,sizeof(a)就是数组名单独放在sizeof内部,计算的数组总大小,单位是字节
printf("%d\n",sizeof(a+0));//4/8,a+0 其实是数组首元素的地址
printf("%d\n",sizeof(*a));//4,a是数组首元素的地址 - &a[0],*a -> *&a[0] -> a[0]
printf("%d\n",sizeof(a+1));//4/8,a是数组首元素的地址 -- int*,a+1 跳过1个整型, 是第二个元素的地址
printf("%d\n",sizeof(a[1]));//4
printf("%d\n",sizeof(&a));//4/8,&a - 取出的是数组的地址,但是数组的地址也是地址呀,是地址大小就是4/8字节
printf("%d\n",sizeof(*&a));//16
printf("%d\n",sizeof(&a+1));//4/8,&a --> int (*)[4],&a+1 跳过一个数组
printf("%d\n",sizeof(&a[0]));//4/8,取出首元素的地址
printf("%d\n",sizeof(&a[0]+1));//4/8,第二个元素的地址
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//6
printf("%d\n", sizeof(arr+0));//4/8,arr+0是数组首元素的地址
printf("%d\n", sizeof(*arr));//1,*arr是首元素的,计算的是首元素的大小
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8,&arr是数组的地址
printf("%d\n", sizeof(&arr+1));//4/8,&arr + 1跳过一个数组后的地址
printf("%d\n", sizeof(&arr[0]+1));//4/8,第二个元素的地址
printf("%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));//随机值
printf("%d\n", strlen(&arr+1));//随机值-6
printf("%d\n", strlen(&arr[0]+1));随机值-1
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr+0));//4/8
printf("%d\n", sizeof(*arr));//1,*arr -是数组首元素
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8,数组的地址
printf("%d\n", sizeof(&arr+1));//4/8
printf("%d\n", sizeof(&arr[0]+1));//4/8
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr+0));//6
printf("%d\n", strlen(*arr));//非法访问
printf("%d\n", strlen(arr[1]));//非法访问
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr+1));//随机值
printf("%d\n", strlen(&arr[0]+1));//5
char *p = "abcdef";
printf("%d\n", sizeof(p));//4/8
printf("%d\n", sizeof(p+1));4/8
printf("%d\n", sizeof(*p));1
printf("%d\n", sizeof(p[0]));//1
printf("%d\n", sizeof(&p));//4/8
printf("%d\n", sizeof(&p+1));//4/8
printf("%d\n", sizeof(&p[0]+1));//6
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p+1));//5
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));//5
//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));//48,a这个二维数组的数组名单独放在sizeof内部,计算整个数组的大小
printf("%d\n",sizeof(a[0][0]));//4,第一行第一个元素
printf("%d\n",sizeof(a[0]));//16,a[0] 第一行的数组名,这时数组名单独放在sizeof内部了
printf("%d\n",sizeof(a[0]+1));//4,a[0]不是单独放在sizeof内部,a[0]表示的首元素的地址,即第一行第一个元素的地址 - &a[0][0],a[0] + 1 是第一行第2个元素的地址 &a[0][1]
printf("%d\n",sizeof(*(a[0]+1)));//4,a[0][1]
printf("%d\n",sizeof(a+1));//a作为二维数组的数组名并非单独放在sizeof内部,所以表示首元素的地址,二维数组的首元素是第一行,这里的a就是第一行的地址--- int (*)[4],a+1是跳过第一行,指向了第二行
printf("%d\n",sizeof(*(a+1)));//16,*(a+1)-->a[1]
printf("%d\n",sizeof(&a[0]+1));//4/8,&a[0]是第一行的地址,&a[0]+1是第二行的地址
printf("%d\n",sizeof(*(&a[0]+1)));//16,a[1]
printf("%d\n",sizeof(*a));//16,*a - 就是第一行,*a -- *(a+0) -- a[0]
printf("%d\n",sizeof(a[3]));//16
//sizeof()里的值并不会进行计算
笔试题