目录
指针进阶::
指针进阶知识点:
1.字符指针
在指针的类型中我们知道有一种指针类型为字符指针 : char *
一般使用:
#include<stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'b';
printf("%c\n", ch);
return 0;
}
还有一种使用方式如下:
#include<stdio.h>
int main()
{
const char* p = "abcdef";//赋值给p的是字符串首字符的地址
printf("%s\n", p);
return 0;
}
本质:const char * pstr = "hello world";本质是把字符串hello world首字符的地址放到了pstr中.
上面代码的意思是把一个常量字符串首字符h的地址存放到指针变量pstr中.
面试题
//下列程序的运行结果为:
#include<stdio.h>
int main()
{
const char* p1 = "abcdef";
const char* p2 = "abcdef";
if (p1 == p2)
{
printf("p1 == p2\n");
}
else
{
printf("p1 != p2\n");
}
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (arr1 == arr2)
{
printf("arr1 == arr2\n");
}
else
{
printf("arr1 != arr2\n");
}
return 0;
}
//程序的运行结果为:p1 == p2 arr1 != arr2
这里p1和p2指向的是同一个常量字符串,C/C++会把常量字符串存储到单独的一个内存区域(数据区),当几个指针指向同一个字符串的时候,它们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块,所以p1==p2 arr1!=arr2.
2.指针数组
指针数组的定义:
指针数组是用来存放指针的数组,指针数组作为实参传递给形参时,形参要用二级指针来接收.
int* arr1[10];//整型指针的数组
char* arr2[4];//一级字符指针的数组
char** arr3[5];//二级字符指针的数组
指针数组的应用:模拟实现二维数组
#include<stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[3] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
//printf("%d ", parr[i][j]);
printf("%d ", *((parr[i] + j)));
}
printf("\n");
}
return 0;
}
3.数组指针
数组指针的定义:
数组指针是指针,好比整型指针是用来存放整形的指针,字符指针是用来存放字符的指针,那么数组指针也就是存放数组的指针.
数组指针的表示形式:
int (*p) [10]:p先和 * 结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组,所以p是一个指针,指向一个数组,叫数组指针.
注意:[ ]的优先级要高于 * 号的,所以必须加上( )来保证p先和 * 结合.
&数组名 VS 数组名
int main()
{
char* arr[5] = { 0 };//char* (*p)[5] = &arr;
char ch = 'w';
char* p1 = &ch;//一级指针存储变量的地址
char** p2 = &p1;//二级指针存储一级指针变量的地址
return 0;
}
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
//int* p = &arr;有警告的
int(*p2)[10] = &arr;
//p2的类型为int(*)[10]
printf("%p\n", arr);
printf("%p\n", arr + 1);
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0] + 1);
printf("%p\n", &arr);
printf("%p\n", &arr + 1);
int sz = sizeof(arr);
printf("%d\n", sz);
return 0;
}
注:数组名通常表示的都是首元素的地址,但是有两个例外:
1.sizeof(数组名):这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节.
2.&数组名:这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址.
实际上&arr表示的是数组的地址,而不是数组首元素的地址,本例中&arr的类型是:int( * )[10],
是一种数组指针类型,数组的地址+1,跳过整个数组的大小,所以&arr+1相对于&arr的差值是40.
注意:二维数组的首元素是它的第一行,二维数组的数组名表示的是整个第一行一维数组的地址.
数组指针在一维数组中的使用(不推荐):
这里简单介绍一下数组指针在一维数组中的使用,但数组指针不推荐在一维数组使用.
原因在于:数组指针将一维数组升维,一级指针变成二级指针,使其更加复杂化.
#include<stdio.h>
int main()
{
//代码1
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//int* p = arr;
//int i = 0;
//for (i = 0; i < 10; i++)
//{
// printf("%d ", *(p + i));
//}
//printf("\n");
//代码2 不推荐
int(*p)[10] = &arr;
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
//p是指向数组的 *p其实就相当于数组名 数组名又是首元素的地址 所以*p本质上是数组首元素的地址
printf("%d ", *( * p + i));
}
printf("\n");
return 0;
}
数组指针在二维数组中的使用:
//打印二维数组(数组指针多用于二维数组或三维数组)
#include<stdio.h>
void print1(int arr[3][5],int row,int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print2(int(*p)[5], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
print1(arr, 3, 5);
printf("\n");
print2(arr, 3, 5);
return 0;
}
数组指针的性质:
1. 数组指针+1跳过整个一维数组.
2. 数组指针解引用得到的是数组首元素的地址.
3. 数组指针[ ]的数字不可省略.
注: 1. 形参的一维数组的行可以省略,二维数组行也可以省略但列不可以省略.
2. int(*p)[5],p的类型是int(*)[5],p是指向一个整型数组的,数组5个元素,即int[5].
3. 二维数组的数组名是第一行的地址,相当于一个一维数组的地址,所以用数组指针接收
4. p+1跳过了一个5个元素的数组
练习:
int arr[5] //arr是整型数组
int* parr1[10] //parr1是整型指针数组
int(*parr2)[10] //parr2数组指针
int(*parr3[10])[5] //parr3是存放数组指针的数组
4.数组和指针传参
数组的地址不能用二级指针接收的原因:
5.函数指针
函数指针的引入:
首先看一段代码:
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
注:对于函数来说 单独的函数名和取地址函数名都表示函数的地址.
函数指针的定义与函数调用的三种表示形式:
函数指针:函数指针也是一种指针,一种指向函数的指针.(数组谈存放,指针谈指向)
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int, int) = &Add;
//int(*pf)(int, int) = Add;
int ret = Add(2, 3);
printf("%d\n", ret);
ret = (*pf)(2, 3);
printf("%d\n", ret);
ret = pf(2, 3);
printf("%d\n", ret);
return 0;
}
来自《C陷阱与缺陷》两个有趣的代码之深度剖析
函数指针的用途:模拟实现计算器
//对上述代码进行优化
void menu()
{
printf("*****************************\n");
printf("****** 1.add 2.sub ******\n");
printf("****** 3.mul 4.div ******\n");
printf("****** 0.exit ******\n");
printf("*****************************\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
void calc(int(*pf)(int, int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入两个操作数:->");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("%d\n", ret);
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择:->");
scanf("%d", &input);
switch (input)
{
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 4:
calc(Div);
break;
case 0:
printf("退出计算器\n");
default:
printf("选择错误\n");
}
} while (input);
return 0;
}
6.函数指针数组
函数指针数组的定义:
函数指针存放的是函数的地址,函数指针数组存放的是多个函数的地址.
函数指针数组的表示形式:
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int(*pf)(int, int) = Add;
int(*pfArr[4])(int, int) = { Add,Sub,Mul,Div };
for (int i = 0; i < 4; i++)
{
printf("%d\n", pfArr[i](8, 4));
}
return 0;
}
函数指针数组的用途:
函数指针数组模拟实现计算器
#include<stdio.h>
void menu()
{
printf("*****************************\n");
printf("****** 1.add 2.sub ******\n");
printf("****** 3.mul 4.div ******\n");
printf("****** 0.exit ******\n");
printf("*****************************\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
//函数指针数组
int(*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
do
{
menu();
printf("请选择:->");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
}
else if (input >= 1 && input <= 4)
{
printf("请输入两个操作数:->");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("%d\n",ret);
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
7.指向函数指针数组的指针
指向函数指针数组的指针的表示方法:
将数组名和方括号隔开,用星号括起来,变成指针.
注:指向函数指针数组里存放的是取地址函数指针数组的数组名.
#include<stdio.h>
int main()
{
int (*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
int(*(*pfArr)[5])(int, int) = &pfArr;
return 0;
}
8.回调函数之模拟实现qsort函数
冒泡排序的优化:
void BubbleSort(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int flag = 1;//假设数组已经排好序
int j = 0;//一趟冒泡排序的过程
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
万能指针:
#include<stdio.h>
int main()
{
int a = 10;
char* pa = (char*)&a;
void* pv = &a;
return 0;
}
深入理解qsort函数:
qsort函数的功能:qsort函数可以排序任意类型的数据
qsort函数的参数:
qsort函数的返回值:
qsort函数的使用:
用qsort函数排序整型数据:
int cmp_int(const void* e1, const void* e2)
{
return (*(int*)e1 - *(int*)e2);
}
//测试使用qsort来排序整型数据
void test1()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//把数组排成升序
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
test1();
return 0;
}
用qsort函数排序结构体类型数据:
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
//字符串比较函数strcmp的返回值为>0 ==0 <0
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int cmp_stu_by_age(const void* e1, const void* e2)
{
return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
//测试使用qsort来排序结构体数据
void test2()
{
struct Stu s[] = { {"zhangsan",15},{"lisi",30},{"wangwu",25} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
int main()
{
test2();
return 0;
}
回调函数:
回调函数就是一个通过函数指针回头再调用所指向的函数,如果你把函数的指针(地址)作为参数传递给函数指针,当这个指针回头被用来调用其所指向的函数时,我们就说这是回调函数,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,,用于对该事件或条件进行响应.
用回调函数模拟实现qsort(冒泡排序版)
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
void BubbleQsort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int flag = 1;
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j+1) * width) > 0)
{
//交换
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
对qsort(冒泡函数函数版)进行测试排序整形数据:
int cmp_int(const void* e1, const void* e2)
{
return (*(int*)e1 - *(int*)e2);
}
//测试冒泡排序版qsort排序整型
void test3()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//把数组排成升序
int sz = sizeof(arr) / sizeof(arr[0]);
BubbleQsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
test3();
return 0;
}
对qsort(冒泡函数函数版)进行测试排序结构体类型数据:
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{
//字符串比较函数strcmp的返回值为>0 ==0 <0
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
int cmp_stu_by_age(const void* e1, const void* e2)
{
return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
void test4()
{
struct Stu s[] = { {"zhangsan",15},{"lisi",30},{"wangwu",25} };
int sz = sizeof(s) / sizeof(s[0]);
BubbleQsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
BubbleQsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
int main()
{
test4();
return 0;
}
9.指针和数组面试题解析
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(&a + 1));//4/8
//&a取出的是数组的地址 存放在数组指针中&a + 1是从数组a的地址向后跳过了一个数组的大小 &a + 1还是地址 是地址就是4/8个字节
printf("%d\n", sizeof(&a[0]));//4/8
//&a[0]就是取出第一个元素的地址 计算的是地址的大小
printf("%d\n", sizeof(&a[0] + 1));//4/8
//&a[0] + 1是第二个元素的地址 大小是4/8个字节
printf("%d\n", sizeof(a));//16
//sizeof(数组名) 数组名表示整个数组 计算的是整个数组的大小 单位是字节
printf("%d\n", sizeof(a + 0));//4/8
//a不是单独放在sizeof内部 也没有取地址 所以a就是首元素的地址 a + 0还是首元素的地址 是地址大小就是4 / 8个字节
printf("%d\n", sizeof(*a));//4
//*a中的a数组首元素的地址 *a就是对首元素地址解引用 找到的就是首元素 首元素的大小是数据1 大小为4个字节
printf("%d\n", sizeof(a + 1));//4/8
//这里的a是首元素的地址 a + 1是第二个元素的地址 sizeof(a + 1)就是4/8个字节
printf("%d\n", sizeof(a[1]));//4
//计算的是第二个元素的大小
printf("%d\n", sizeof(&a));//4/8
//&a取出的是数组的地址
printf("%d\n", sizeof(*&a));//16
//&a拿到的是数组名的地址 类型是int(*)[4] 是一种数组指针 数组指针1解引用找到的是整个数组
return 0;
}
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//6
//sizeof(数组名)计算的是整个数组的大小
printf("%d\n", sizeof(arr + 0));//4/8
//arr + 0表示的是数组首元素的地址
printf("%d\n", sizeof(*arr));//1
//*arr得到的是数组的首元素 大小为1个字节
printf("%d\n", sizeof(arr[1]));//1
//arr[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
//&arr[0] + 1取出的是第二个元素的地址
return 0;
}
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
//strlen是求字符串长度的 关注的是字符串的\0 计算的是\0之前出现的字符的个数
printf("%d\n", strlen(arr));//随机值
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
return 0;
}
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr + 0));//4/8
printf("%d\n", sizeof(*arr));//1
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
return 0;
}
int main()
{
char arr[] = "abcdef";
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
return 0;
}
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p)); //p里面存储的是字符a的地址 是地址就是4个或8个字节
printf("%d\n", sizeof(p + 1)); //p + 1里面存储的是字符b的地址 是地址就是4个或8个字节
printf("%d\n", sizeof(*p)); //*p里面存储的是字符a 字符a所占内存大小为1个字节
printf("%d\n", sizeof(p[0])); //p[0]相当于* (p + 0)里面存储的是字符a 所占内存大小为1个字节
printf("%d\n", sizeof(&p)); //&p相当于一个二级指针 p里面存储的是字符a的地址 &p存储的是一级指针变量的地址
printf("%d\n", sizeof(&p + 1)); //&p + 1跳过了整个一级指针
printf("%d\n", sizeof(&p[0] + 1)); //&和*号抵消 p仍然是一级指针 存储字符a的地址 加一相当于存放的是字符b的地址
printf("%d\n", strlen(p)); //p里面存储的是字符a的地址 字符串长度为6
printf("%d\n", strlen(p + 1)); //p + 1里面存储的是字符b的地址 字符串长度为5
printf("%d\n", strlen(*p)); //野指针
printf("%d\n", strlen(p[0])); //野指针
printf("%d\n", strlen(&p)); //在一级指针变量的空间中寻找\0 字符串长度为随机值
printf("%d\n", strlen(&p + 1));//在一级指针变量的空间中寻找\0 理论为strlen(&p) - 4但是在这4个字节的空间内不确定系统会不会放\0因此和strlen(&p)没有必然的联系
printf("%d\n", strlen(&p[0] + 1)); //&和*号抵消 p仍然是一级指针 存储字符a的地址 字符串长度为5
return 0;
}
//二维数组
//对于一维数组 sizeof(arr)表示整个一维数组的大小 sizeof(arr + 1)表示跳过一个元素
//对于二维数组arr[0]单独放到sizeof内部 arr[0]代表第一行整个一维数组的大小 arr[0] + 1表示跳过第一行一维数组中的一个元素 指向第二个元素
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); //整个二维数组的大小:48个字节
printf("%d\n", sizeof(a[0][0])); //第一个元素 4个字节
printf("%d\n", sizeof(a[0])); //a[0]是第一行的数组名 代表首元素的地址a[0]等价于*(a + 0)但a是二维数组的数组名 一个*只能解一层引用 单独放到sizeof内部 代表整个第一行16个字节
printf("%d\n", sizeof(a[0] + 1)); //a[0]相当于&a[0][0] 所以a[0] + 1代表第一行第二个元素的地址 4或8个字节
printf("%d\n", sizeof(*(a[0] + 1))); //a[0] + 1解引用表示第一行第二个元素 大小为4个字节
printf("%d\n", sizeof(a + 1)); //a虽然是二维数组的地址 但是并没有单独放在sizeof内部 也没取地址 所以a表示首元素的地址
//而二维数组的首元素是它的第一行 a就是第一行的地址 第一行的地址 + 1跳过第一行 指向第二行 表示第二行的地址 4个或8个字节
printf("%d\n", sizeof(*(a + 1))); //a + 1表示第二行的地址 存放到数组指针中 对a + 1解引用表示整个第二行 16个字节
printf("%d\n", sizeof(&a[0] + 1)); //对第一行的数组名取地址拿出的是第一行的地址 &a[0] + 1得到的是第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1))); //&a[0] + 1得到的是第二行的地址 再解引用 得到第二行的大小 16个字节
printf("%d\n", sizeof(*a)); //二维数组的数组名表示首元素的地址 就是第一行的地址 *a拿到的是第一行 16个字节
printf("%d\n", sizeof(a[3])); //a[3]是第四行的数组名 单独放到sizeof内部 表示第四行的元素大小 16个字节
return 0;
}
指针笔试题解析:
//下列程序的运行结果是:
int main()
{
int a[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
//将数组指针强制类型转换为整型指针 强制类型转换的值不变 只是类型发生改变
//假设p的值为0x100000
//下列程序的运行结果为:
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); //结构体指针 + 1跳过一个结构体
printf("%p\n", (unsigned long)p + 0x1); //无溢出
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
//下列程序的运行结果为:
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;
}
//下列程序的运行结果为:
int main()
{
int a[3][2] = { (0,1),(2,3),(4,5) };
int* p;
p = a[0];
printf("%d", p[0]); //a[0]单独放在sizeof内部才表示二维数组一行的大小 现在表示首元素的地址
return 0;
}
//下列程序的运行结果为:
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;
}
//指针和指针相减得到的是两个指针之间的元素个数 但要注意正负号 因为数组的创建是由低地址到高地址存储的
//%p打印负数直接将负数的补码4位一隔4位一隔转化成16进制 %u打印负数操作的也是负数的补码 %d打印的是原码
//下列程序的运行结果为 :
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));//6的地址强制类型转化为整型指针没必要
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
//下列程序的运行结果为:
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
//下列程序的运行结果为:
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;
}
C语言编程训练(牛客网)
BC37—网购
#include <stdio.h>
int main()
{
double price = 0.0;
int month = 0;
int date = 0;
int flag = 0;
scanf("%lf %d %d %d", &price, &month, &date, &flag);
if (month == 11 && date == 11)
{
price = price * 0.7 - flag * 50;
}
else if (month == 12 && date == 12)
{
price = price * 0.8 - flag * 50;
}
if (price < 0.0)
{
printf("%.2lf\n", 0.00);
}
else
{
printf("%.2lf\n", price);
}
return 0;
}
BC60—带空格直角三角形图案
#include <stdio.h>
int main()
{
int n = 0;
while (scanf("%d", &n) == 1)
{
int i = 0;
int j = 0;
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
if (i + j < n - 1)
{
printf(" ");
}
else
{
printf("* ");
}
}
printf("\n");
}
}
return 0;
}
BC100—有序序列合并
int main()
{
int n = 0;
int m = 0;
scanf("%d %d", &n, &m);
int arr1[n];
int arr2[m];
int arr3[m + n];
for (int i = 0; i < n; i++)
{
scanf("%d ", &arr1[i]);
}
for (int i = 0; i < m; i++)
{
scanf("%d", &arr2[i]);
}
int begin1 = 0;
int begin2 = 0;
int begin3 = 0;
while (begin1 <= n - 1 && begin2 <= m - 1)
{
if (arr1[begin1] < arr2[begin2])
{
arr3[begin3] = arr1[begin1];
begin1++;
begin3++;
}
else
{
arr3[begin3] = arr2[begin2];
begin2++;
begin3++;
}
}
while (begin1 <= n - 1)
{
arr3[begin3] = arr1[begin1];
begin1++;
begin3++;
}
while (begin2 <= m - 1)
{
arr3[begin3] = arr2[begin2];
begin2++;
begin3++;
}
for (int i = 0; i < m + n; i++)
{
printf("%d ", arr3[i]);
}
return 0;
}
BC116—小乐乐改数字
#include <stdio.h>
#include<math.h>
int main()
{
int input = 0;
int sum = 0;
scanf("%d", &input);
int i = 0;
while (input)
{
int bit = input % 10;
if (bit % 2 == 1)
{
sum += 1 * pow(10, i);
i++;
}
else
{
sum += 0 * pow(10, i);
i++;
}
input /= 10;
}
printf("%d\n", sum);
return 0;
}
调整数组使奇数全部都位于偶数前面
void move_odd_even(int arr[], int sz)
{
int left = 0;
int right = sz - 1;
while (left < right)
{
while ((left < right) && (arr[left] % 2 == 1))
{
left++;
}
while ((left < right) && (arr[right] % 2 == 0))
{
right--;
}
if (left < right)
{
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
}
int main()
{
int arr[10] = { 0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
scanf("%d", &arr[i]);
}
move_odd_even(arr, sz);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}