(创建于2017/8/17)
指针的含义
#include<stdio.h>
int main()
{
int a = 10;
int *p = &a; //这行代码等同于 int *p; p = &a;
//int *p;
//*p = &a; //这种方式是错误的,这两行的意思就是定义以一个指针,但是没有
//具体的指向,也就是一个野指针,然后给这个野指针指向的内容(*p就是指向的内容的意思)
//赋值,也就是将a变量的地址赋值给p指向的内容,但实际上,p没有具体的指向,所以这样是错误的
//*p //代表指针所指内存的实际数据,切记,指针只能存放地址,不能将一个int
//型的变量直接赋值给一个指针,这样是没有意义的 比如 int *p = 100;
return 0;
}
指针的步长
指针运算,不是简单的整数加减法,而是指针指向的数据类型在内存中占用字节数做为倍数的运算
#include <stdio.h>
int main() {
int a;
int *p = &a;
printf("p: %d,p+1:%d\n",p,p+1);
char b;
char *q = &b;
printf("q: %d,q+1:%d\n", q, q + 1);
system("pause");
}
打印
p: 5962300,p+1:5962304
q: 5962279,q+1:5962280
请按任意键继续. . .
#include<stdio.h>
int main()
{
int a = 100000000;
printf("%d\n",sizeof(a)); //得到4,int占四个字节,也就是说sizeof得到的是字节数
return 0;
}
1.取地址符 &
#include<stdio.h>
int main()
{
int a = 190;
printf("%p\n", &a); //取出a的地址,%p输出指针的地址
return 0;
}
#include<stdio.h>
int main()
{
int a = 190;
printf("%p\n", &a);
int *p = &a;
printf("%X\n", p); //a的地址赋值给变量p,将地址格式化为十六进制形式,以大写表示,
int *p1; //第一一个变量,名字叫p1,他可以指向一个int类型的地址
p1=&a; //指针变量的值一般不能直接赋值一个整数,而是通过取变量地址的方式
return 0;
}
2.p代表的意思(号,定义指针的时候表示一种类型,使用的时候表示操作指向的内存)
#include<stdio.h>
int main()
{
int a = 1;
int *p;
p = &a;
int b = *p; //p指向a的地址,*p的意思是指针变量指向的内容
printf("%X\n", b);
*p = 2; //通过指针间接修改制定内存地址的值,
return 0;
}
通过指针修改了a的值
#include<stdio.h>
int main()
{
int a = 10;
int *p;
p = &a;
*p = 1;
printf("%d\n",a);
return 0;
}
输出结果a=1
3.数组中元素地址的特点
int main()
{
char buf[10];
printf("%u,%u,%u,%u\n", buf, &buf[0], &buf[1], &buf[2]);
printf("---------------------------------------------------\n");
int buf1[10];
printf("%u,%u,%u,%u\n", buf1, &buf1[0], &buf1[1], &buf1[2]);
return 0;
}
输出结果:(char每个元素占一个字节,int每个元素占四个字节,可以从地址的连续性上看出来)
16645708, 16645708, 16645709, 16645710 //可见数组的地址就是数组中第一个元素的地址
16645660, 16645660, 16645664, 16645668
请按任意键继续. . .
4.无类型指针(万能指针)
void p
定义一个指针变量,但不指定他指向的哪种数据类型,可以通过强制转换将void转换为其他指针类型,也可以用void将其他指针类型转换为void指针类型
1 不可以定义void类型的普通变量,不能确定类型
2 可以定义void变量,void指针也叫万能指针
3 void可以指向任何类型的变量,使用指针所指向的内存时,最后转换为它本身的指针类型
#include <stdio.h>
int main() {
void *p = NULL;
int a = 10;
p = &a;
*((int*)p) = 20;
printf("*p = %d\n", *((int*)p));
printf("a = %d\n", a);
system("pause");
}
打印
*p = 20
a = 20
请按任意键继续. . .
5.指针的大小
指针不管指向哪种数据类型,他的大小都是一定的,不过与编译器有关
32位编译器用32位大小(4字节)保存
64位编译器用64位大小(8字节)保存
#include<stdio.h>
int main()
{
int a = 3;
char c = 0;
int *p = &a;
char *p2 = &c;
printf("%x\n", sizeof(p));
printf("%x\n", sizeof(p2));
return 0;
}
输出结果:
4
4
请按任意键继续. . .
6.空指针与野指针
- 只有定义后的变量,此变量的地址才是合法的
- 野指针就是保存没有意义地址的指针变量或者干脆没有保存地址
- 操作野指针本身不会有任何问题
- 操作野指针所指向的内存才导致错误,比如你打印一个随手指定的地址没有错,但是操作这个地址上的内存就会有问题
#include<stdio.h>
int main()
{
int *p;
p = NULL; //空指针,NULL就是数字0
return 0;
}
#include<stdio.h>
#pragma warning(disable:4700)
int main()
{
int a = 10;
int *p; //p没有具体的指向(或者你随手指定一个地址给他),是野指针(这个无法运行)
*p = 1;
printf("%d\n",a);
return 0;
}
7.指针的兼容性
指针之间赋值比普通数据类型赋值检查更为严格,例如:不能把double赋值给int
原则上一定是相同类型的指针指向相同类型的变量地址,不能用同一种类型的指针指向另一种类型的变量地址
#include<stdio.h>
int main()
{
float a = 3.14;
int i = a; //自动数据类型转换,舍去小数点部分
printf("%d\n", i);
int *p = &a; //错误,指针类型不兼容,打印结果无厘头
printf("%d\n",*p);
return 0;
}
输出结果:
3
1078523331
请按任意键继续. . .
8.指向常量的指针和指针常量
指向常量的指针
const int* p = &a const 修饰,代表指针所指向的内存是只读的,无法通过指针操作这个内存,比如说不能通过p修改内存的值,但是这个地址可以改变,p可以指向另一个变量的内存
int* const p2 = &a const修饰指针变量,代表指针变量的值为只读,表示指针指向的地址是不能改变的,但是这个地址的值可以改变
#include <stdio.h>
int main() {
int a = 10;
int b = 10;
int *p1 = &a;
*p1 = 100; //操作a的内存
p1 = NULL; //操作指针变量p,不会影响到a的内存
printf("a = %d\n", a);
//const 修饰*,代表指针所指向的内存是只读的
//(等价于int const *p2 = &a,两种写法是相同的)
//表示p2指向地址的值不能改变,但是这个地址可以改变
const int *p2 = &a;
//*p2 = 200;无法修改内存的值,相当于
p2 = NULL;
//const修饰指针变量,代表指针变量的值为只读
//表示p4指向的地址是不能改变的,但是这个地址的值可以改变
int* const p4 = &a;
//设置p4指向的地址的值为100
*p4 = 100;
//修改p4指向的地址的值,这时错误的
//p4 = NULL; //err
system("pause");
}
#include<stdio.h>
int main()
{
int a = 10;
const int *p = &a;
//*p = 30; //不能通过*p的方法修改一个const指针,运行会提示错误,
//表达式必须是可修改的左值,const修饰则无法修改(有一种方式可以达到修改的目的,这是C语言的一个不够严谨的地方,看下边)
a = 20;
printf("%d\n",*p); //*p是个只读的值,不能修改
return 0;
}
#include<stdio.h>
int main()
{
int a = 100;
const int *p = &a;
int *p2 = p; //(p = &a) 我们用另一个指针指向p,也就是指向了p所指向的a的地址
//然后修改*p2的值达到修改a的值的目的(这是C语言的一个不够严谨的地方,c++中是不可以的)
*p2 = 200;
printf("%d\n", a);
return 0;
}
同样道理
#include<stdio.h>
int main()
{
const int a = 100;
//a = 200;错误,无法修改
int *p = &a;
//在Windows上修改成功,Mac上xcode无法修改成功,打印的仍是100,并且会有警告
*p = 200;
printf("%d\n", a);
system("pause");
}
指针常量(一旦定义它所指向的地址无法发生改变,但是该地址的值可以改变,这和上边指向常量的指针恰恰相反,指针常量可以修改值不能修改地址,指向常量 的指针可以修改地址,但是不能修改值)
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int *const p = &a; //定义一个常量指针,可以通过常量指针修改或者
*p = 20; //读取一个变量的值
//p = &b; //常量指针一旦定义了,就不能修改其指向的变量
printf("%d\n",*p);
return 0;
}
9.指针与数组的关系
注意,二级指针不是二维数组
#include <stdio.h>
int main() {
int a[3] = {1,2,3};
//指针数组,是数组,每个元素都是指针
int *p[3];
int n = sizeof(p) / sizeof(p[0]);
int i = 0;
for (; i < n; i++) {
//等价于p[i]=a+i;
p[i] = &a[i];
}
for ( i = 0; i < n; i++)
{
//p[i]存储的是地址,所以*p[i]是这个地址的值
printf("*p[i] = %d,p[i]=%d\n", *p[i],p[i]);
}
system("pause");
}
#include <stdio.h>
int main() {
int a = 10;
int *p = &a;
//通过指针间接操作a的内存
*p = 111;
printf("a = %d\n",a);
printf("*p = %d\n", *p);
printf("p[0] = %d\n", p[0]);
//*p等价于*(p+0),同时等价于p[0]
p[0] = 250;
printf("a2=%d\n",a);
//等价于*(p+1),操作野指针所指向的内存,可能会报错
p[1] = 111;
printf("p[1] = %d\n", p[1]);
system("pause");
}
#include<stdio.h>
void printbuf(char buf[])
{
for (int i = 0; i < 10; i++)
{
printf("buf[%d]=%d\n", i, buf[i]);
}
}
int main()
{
char buf[10] = {1,2,3,4,5,6};
//int n = sizeof(buf)/sizeof(*buf);可以得到数组的长度
char *p = buf;
char *p1 = &buf[0];
char *p2 = &buf[1];
char *p3 = &buf[2];
*p2 = 7; //将数组中的第二个值修改为7
p3 += 1; //数组在内存中的地址是连续的,char数组每个元素一个字节,p3
*p3 = 100; //表示buf[2]的地址,加一就是表示buf[3]的地址,然后将buf[3]的值修改为100
p3 -= 2;
*p3 = 12;
printbuf(buf);
return 0;
}
打印
buf[0]=1
buf[1]=12
buf[2]=3
buf[3]=100
buf[4]=5
buf[5]=6
buf[6]=0
buf[7]=0
buf[8]=0
buf[9]=0
请按任意键继续. . .
#include<stdio.h>
int main()
{
int buf[10] = {1,2,3,4,5,6,7,8,9};
int *p = buf; //指向这个数组,也就是指向数组中的第一个元素的地址
p++; //p = p +1; 指向了数组中第二个元素
//buf++; //这样写是错误的,它等同于buf = buf + 1;但是buf是一个常量,不能做左值
int *p2 = buf + 1; //这样是可以的,等同于p2++,p2会指向数组中第二个元素
//int *p2 = buf++; //这样也是错误的,同buf++
return 0;
}
#include<stdio.h>
int main()
{
int buf[10] = {1,2,3,4,5,6,7,8,9};
int *p = buf;
int i;
for ( i = 0; i < 10; i++)
{
printf("%d\n",p[i]); //C语言允许指针通过数组下标的方法访问数组成员
printf("%d\n", *(p + i)); //等同于上边的代码
}
return 0;
}
#include<stdio.h>
int main()
{
int a = 100000000;
char *p = &a;
printf("%x,%x,%x,%x\n",*p,*(p+1),*(p+2),*(p+3));
printf("%x,%x,%x,%x\n", p[0], p[1],p[2],p[3]); //两者等同
return 0;
}
10.指针求字符串的长度和合并字符串
#include<stdio.h>
int main()
{
char a[100] = "hello";
char b[100] = "world";
char *p1 = a;
char *p2 = b;
int len = 0;
while (*p1) //值=0的时候循环退出,=0说明到达了字符串末尾,因为字符串以0结尾
{
p1++;
len++;
}
printf("%d\n", len);
while (*p2)
{
*p1 = *p2; //将p2所在地址的值赋给当前p1所在的位置
p2++;
p1++;
}
printf("%s\n", a); //p1指向的是数组a,修改的值是数组a的值
return 0;
}
值传递
不会改变实参的值
#include <stdio.h>
#include <string.h>
void printbuf(char buf[])
{
for (int i = 0; i < 10; i++)
{
printf("buf[%d]=%d\n", i, buf[i]);
}
}
void swap(int m, int n) {
int temp;
temp = m;
m = n;
n = temp;
printf("m=%d,n=%d\n", m, n);
}
int main() {
int a = 11;
int b = 12;
swap(a, b);
printf("a=%d,b=%d\n", a, b);
system("pause");
}
地址传递
传递的是地址,会改变实参的值
#include <stdio.h>
void swap(int* m, int* n) {
int temp;
temp = *m;
*m = *n;
*n = temp;
printf("m=%d,n=%d\n", *m, *n);
}
int main() {
int a = 11;
int b = 12;
swap(&a, &b);
printf("a=%d,b=%d\n", a, b);
system("pause");
}
形参中传递的数组
1.形参中的数组,是指针变量
2.形参数组:int a[10000], int a[],int *a,对编译器来讲没有任何区别,都是当作int指针处理
3.形参中的数组和非形参中的数组区别:形参中数组是指针,非形参数组是数组
//#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void print_array(int *buf, int n)
{
for (int i = 0; i < n; i++)
{
printf("buf[%d]=%d\n", i, buf[i]);
}
}
//形参中的数组不是数组,是指针变量
void pintf_array2(int buf[10])
{
int n = sizeof(buf) / sizeof(buf[0]);
for (int i = 0; i < n; i++)
{
printf("n=%d,sizeof(buf)=%d,sizeof(buf[0])=%d\n", n,sizeof(buf)), sizeof(buf[0]);
printf("buf[%d]=%d\n", i, buf[i]);
}
}
int main() {
int a[] = { 1,-2,3,-4,5,-6,7,-7,8,-8,9 };
//在这里这种方式可以得到数组的长度,但是放在上边的位置,通过形参中的数组是获取不到的
//因为形参中的数组只是一个指针,那么sizeof(buf),buf是指针变量,32位操作系统中,指针变量buf的
//大小为4,sizeof(buf[0]),buf[0]是int型的变量,也是4,所以sizeof(buf) / sizeof(buf[0])得到的是1并非
//数组的长度,所以通过指针传递数组操作数组,一定要将数组长度提前得到传递到方法中
int n = sizeof(a) / sizeof(a[0]);
pintf_array2(a);
printf("-----------------------------------------\n");
print_array(a, n);
system("pause");
}
返回局部变量的地址
下边的代码在visual studio和qt中都可以编译通过,也可以获取到p指向的地址,*p的值也的确是所赋的值,然而实际上,这是有问题的,由于方法执行后销毁掉,a的地址其实已经变成了非法地址,这时候去操作这个地址也是非法的,在Linux 64位gcc执行,无法获取到这个a的地址。这跟编译器有关系
#include <stdio.h>
//返回a的地址
int* fun() {
int a;
printf("&a = %p\n", &a);
return &a;
}
int main() {
int *p = NULL;
//p指向a的地址
p = fun();
//fun方法执行完后,自动销毁a变量,所以此时这个a的地址已经是非法地址了
printf("p = %p\n", p);
*p = 100;
printf("*p = %d\n", *p);
system("pause");
}
返回全局变量的地址
#include <stdio.h>
int a;
int* fun() {
printf("&a = %p\n", &a);
return &a;
}
int main() {
int *p = NULL;
p = fun();
printf("p = %p\n", p);
*p = 100;
printf("*p = %d\n", *p);
//直接操作地址,不需要定义中间变量
printf("*fun = %d\n", *fun());
system("pause");
}
打印字符串说明
#include <stdio.h>
int main() {
char str[] = {"hello world"};
//以%s为占位符打印一个字符数组(字符串),得到的是这个字符串的内容
printf("str = %s\n",str);
//以%p为占位符打印一个字符数组(字符串),得到的是这个字符串的地址
printf("str = %p\n", str);
//这样打印是会报错的,因为%s打印字符串就已经做了相应的处理了,不用加*
//printf("str = %s\n",*str);
//str表示字符串中第一个字符的地址,*str表示第一个字符,所以用%c打印可以正常输出
printf("str = %c\n", *str);
//以%s为占位符打印一个字符数组(字符串),其实内部实现就是这样
int i = 0;
while (str[i] != '\0') {
printf("%c", str[i]);
i++;
}
printf("\n");
system("pause");
}
打印:
str = hello world
str = 0055FB1C
str = h
hello world
请按任意键继续. . .
自己实现字符串拷贝函数strcpy
#include <stdio.h>
void my_strcpy(char *dst, char* src) {
int i = 0;
while (*(src + i) != '\0') {
*(dst + i) = *(src + i);
i++;
}
*(dst + i) = 0;
}
int main() {
char src[] = "hello world";
char dst[100];
char *p = dst;
my_strcpy(p, src);
printf("%s\n", p);
system("pause");
}
字符串常量
每个字符串都是一个地址,这个地址是指字符串首元素地址,字符串常量放在data区,文字常量区
#include <stdio.h>
int main() {
//每个字符串都是一个地址,这个地址是指字符串首元素地址
//每个字符串常量放在data区,文字常量区
//直接打印一个字符串,以p为占位符得到的是地址
printf("s1 = %p\n", "hello");
//因为每个字符串都是代表首地址,所以+1之后就是把地址向后移动了一步,
//看打印结果,这两行 代码恰恰证明了每个字符串都是地址这个结论
printf("s1 = %s\n", "hello");
printf("s1 = %s\n", "hello"+1);
//文字常量区的字符串,是只读的不能修改
char *p = "good morning";
//这里会报错
*p = "nice";
printf("*p = %s\n", *p);
system("pause");
}
查找匹配字符串出现的位置
#include <stdio.h>
int main() {
char *p = "11abcd121232abcd23232abcd98y7abcdqqq";
int i = 0;
char *temp = NULL;
while (1) {
//查找匹配的字符串,如果找到,返回匹配字符串的地址,没有找到返回空
temp = strstr(p,"abcd");
if (temp == NULL) {
break;
}
else {
i++;
// 找到一个之后,设置这个字符串的后边位置为下一次寻找的起始位置
p = temp + strlen("abcd");
}
}
printf("个数 = %d\n", i);
system("pause");
}
两头堵模式实现拷贝字符串并去除两端的空字符
#include <stdio.h>
int main() {
char *p = " 1234567 ";
//start指针指向第一个元素地址
char *start = p;
//end指向最后一个元素地址
char *end = p + strlen(p) - 1;
//从左到右,找到第一个不是空的字符位置
while (*start == ' ' && *start != '\0') {
start++;
}
//从右到左,找到第一个不是空的字符位置
while (*end == ' ' && end != p) {
end--;
}
//得到不包含空字符的字符串长度
int n = end - start + 1;
printf("n = %d\n", n);
char buf[100] = "aaaaaaaaaaaaaa";
//拷贝
strncpy(buf, start, n);
buf[n] = 0;
printf("buf = %s\n", buf);
system("pause");
}
11.游戏作弊器
__declspec(dllexport) //添加这个关键字代表go函数是可以在其他程序中调用的dll函数
void go()
{
int *p = 0x1CF88808; //得到植物大战僵尸中阳关变量的地址,这个地址是
while (1) //植物大战僵尸的地址,我自己写的程序没有权利修改其他程序的地址的内容
{
if (*p < 200)
{
*p = 400;
}
}
}
//操作步骤:目前此方法只针对单机游戏有效
/*
1.生成dll动态链接库,项目->属性->配置属性->项目默认值->配置类型 选择动态库(.dll)
2.在顶部添加关键字__declspec(dllexport)
3.修改返回值类型为void,函数名随便改个(这一步非必需)
4.点击本地windows调试器,会在指定目录位置生成.dll文件
5.需要两个工具辅助a:Cheat Engine 6.3
用于获取游戏中相应变量的地址b.DllInject.exe用于将dll文件注入到你要修改的游戏中
7.注入之后游戏修改成功
*/
12.指针的运算
#include<stdio.h>
int main()
{
int buf[10];
int *p = buf; //注意指针指向数组不需要取地址符,但是指向数组中的某一个元素,需要区地址符
int *p1 = &buf[1];
printf("%d,%d\n", p, p1);
p++;
printf("%d\n", p);
return 0;
}
输出结果:
15465412,15465416
15465416
请按任意键继续. . .
为什么p++之后得到的地址比原本增加了4呢?
指针运算不是简单的整数加减法,而是指针指向的数据类型在内存中占用字节数作为倍数的运算
char *p;
p++ 移动了sizeof(char)这么多个字节数
int p;
p++ 移动了sizeof(int)这么多个字节数
p+=3 移动了3sizeof(int)个字节(注意这个int指的是他本身类型的int)
如果此时
p = (int)p+3; 将p强转为一个整数,那么就会按照整数的加法去计算,得到的也就是增加3后的结果,但是这样
对指针是没有意义的
#include<stdio.h>
int main()
{
int buf[10];
int *p = buf;
int *p1 = &buf[1];
printf("%d,%d\n", p, p1);
p++;
printf("%d\n", p);
int *pp1 = &buf[1];
int *pp2 = &buf[3];
printf("%d\n", pp2 - pp1); //结果是2 两个指针相减得到两个数组元素 的相对距离
char *pp3 = &buf[1];
char *pp4 = &buf[3];
printf("%d\n", pp4 - pp3); //结果是8 相差两个int,一个int4个字节,也就是相差八个字节,char类型
//的指针一个char一个字节,那么也就是8个char
long long *pp3 = &buf[1];
long long *pp4 = &buf[3]; //long long 占八个字节,所以结果是1
short *pp3 = &buf[1];
short *pp4 = &buf[3]; //short 占2个字节,所以结果是4(本质上就是有几个short)
printf("%d\n", pp4 - pp3); //不要相加,编译无法通过,没有意义
return 0;
}
指针的一些操作
赋值:int *p = &a;
求值:int i = *p;
取指针地址:int **pp = &p; p本身是一个指针
求差值: p1 -p2 通常用与一个数组内求两个元素的距离
比较: p1 == p2 通常用来比较两个指针是否指向同一个位置
13.通过指针求数组中的最大值
#include<stdio.h>
int max(int arr[])
{
int maxValue = arr[0];
for (int i = 0; i < 10; i++)
{
if (maxValue < arr[i])
{
maxValue = arr[i];
}
}
printf("%d\n", maxValue);
}
int max2(int *s)
{
int maxValue = *s;
for (int i = 0; i < 10; i++)
{
if (maxValue < *(s+i))
{
maxValue = *(s+i);
}
}
printf("%d\n", maxValue);
}
int main()
{
int arr[100] = {1,34,23,45,767,232,12,23,34};
max(arr);
max2(arr);
return 0;
}
14.通过指针将数组逆置
#include<stdio.h>
int reverse(int arr[])
{
int start = 0;
int end = 9;
while (start < end)
{
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
int i;
for (i = 0; i <= 9; i++)
{
printf("%d\n",arr[i]);
}
}
int reverse2(int *arr)
{
int *start = &arr[0];
int *end = &arr[9];
while (start < end)
{
int temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
int i;
for (i = 0; i <= 9; i++)
{
printf("%d\n", arr[i]);
}
}
int main()
{
int arr[100] = {1,34,23,45,767,232,12,23,34,56};
reverse2(arr);
return 0;
}
15.在不对数组排序的条件下,求数组中第二大元素
#include<stdio.h>
void s_max(int arr[])
{
int max;
int s_max;
if (arr[0] > arr[1])
{
max = arr[0];
s_max = arr[1];
}
else
{
max = arr[1];
s_max = arr[0];
}
int i;
for (i = 2; i < 10; i++)
{
if (max < arr[i])
{
s_max = max;
max = arr[i];
}
else if(max > arr[i] && arr[i] > s_max)
{
s_max = arr[i];
}
}
printf("%d\n", s_max);
}
void s_max2(int *s)
{
int max;
int s_max;
if (*s > *(s+1))
{
max = *s;
s_max = *(s + 1);
}
else
{
max = *(s + 1);
s_max = *s;
}
int i;
for (i = 2; i < 10; i++)
{
if (max < *(s+i))
{
s_max = max;
max = *(s + i);
}
else if (max > *(s + i) && *(s + i) > s_max)
{
s_max = *(s + i);
}
}
printf("%d\n", s_max);
}
int main()
{
int arr[100] = {1,34,23,45,767,232,12,23,34,56};
s_max(arr);
printf("---------------------------------------\n");
s_max2(arr);
return 0;
}
16.指针实现冒泡排序
#include<stdio.h>
void bubble(int arr[])
{
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7-i-1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
int i;
for (i = 0; i <7; i++)
{
printf("%d\n", arr[i]);
}
}
void bubble2(int *s)
{
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7 - i - 1; j++)
{
if (*(s+j) > *(s+j+1)) //s指向数组中第一个元素的位置,s+j就是指向数组中第j个元素的地址,*(s+j)就是第j个元素的值
{
int temp = *(s+j);
*(s + j) = *(s + j+1);
*(s + j + 1) = temp;
}
}
}
int i;
for (i = 0; i <7; i++)
{
printf("%d\n", *(s+i)); //注意括号的位置
}
}
int main()
{
int arr[100] = {1,34,23,45,767,232,12,23,34,56};
bubble(arr);
printf("---------------------------------------");
bubble2(arr);
return 0;
}
17.指针实现将英文逆置
#include<stdio.h>
int reverse(char arr[])
{
char *start = &arr[0];
char *end = &arr[strlen(arr) - 1];
while (start < end)
{
char *temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
printf("%s\n", arr);
}
int main()
{
char arr[100] = "hello world";
reverse(arr);
return 0;
}
18.指针实现汉字逆置
#include<stdio.h>
int reverse(char arr[])
{
short *start = &arr[0]; //short刚好占两个字节,在vs中表示汉字正合适
short *end = &arr[strlen(arr) - 2]; //vs中汉字是gbk编码方式,一个汉字占2个字节
//qt中是utf-8编码方式,每个汉字占3个字节,strlen得到的是字节数,假如有四个汉字,strlen结果在vs中就是8,因此这里减的是2
while (start < end)
{
short *temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
printf("%s\n", arr);
}
int main()
{
char arr[100] = "你好世界";
reverse(arr);
return 0;
}
19.多级指针
#include <stdio.h>
#include <string.h>
int main() {
int a = 10;
int *p = &a;
int **q = &p;
int ***t = &q;
int ****m = &t;
printf("*m = %d\n", *m);
printf("t = %d\n", t);
printf("**m = %d\n", **m);
printf("q= %d\n",q);
printf("***m = %d\n", ***m);
printf("p= %d\n", p);
printf("****m = %d\n", ****m);
printf("a= %d\n", a);
system("pause");
}
打印
*m = 12385392
t = 12385392
**m = 12385404
q= 12385404
***m = 12385416
p= 12385416
****m = 10
a= 10
请按任意键继续. . .
从上边的图解中可以看到,首先定义了一个变量a,给他赋值为10,然后定义了一个一级指针p指向他,二级指针q指向p,三级指针t指向q,四级指针m指向t,我们知道,*号用在定义指针的时候表示一种类型,例如int *p = &a 定义一种一级指针类型指向a,在使用的时候表示操作所指向的内存,m指向t所指向的内存,那么*m就是操作m所指向的内存,就是t指向的内存,*m ->t 。那么**m就可以看作*(*m) -> *t,*t同理就是操作t所指向的内存,就是q,那么**m -> q,以此类推
简单应用:英汉词典
#define _CRT_SECURE_NO_WARNINGS
#include<string.h>
#include<stdio.h>
#define MAX 3
struct dict {
char *key;
char *content;
};
void get_dict(struct dict **tmp) {
//在堆区分配MAX个结构体空间
//把一级指针p取地址传入到tmp,这步操作等价于struct dict **tmp = &p;
//tmp是个中间变量,我们是要将p指向申请到的空间,*tmp表示的就是
//指针变量p,所以*tmp = xxx就相当于p = xxx
*tmp = (struct dict *)malloc(MAX * sizeof(struct dict));
if (tmp == NULL) {
return;
}
//上边我们申请了MAX个结构体空间,接下来要在这个空间中申请局部的
//空间来存储每个结构体中的变脸key content
//第一个结构体放第一个单词(用.实现)
//(*tmp)[0]表示第一个结构体变量,strlen("a") + 1表示求字母a的长度,+1是因为后边需要\0
//key申请空间
(*tmp)[0].key = (char *)malloc(strlen("a") + 1);
//存值
strcpy((*tmp)[0].key,"a");
//content申请空间
(*tmp)[0].content = (char *)malloc(strlen("字母A")+1);
//存值
strcpy((*tmp)[0].content,"字母A");
//第二个结构体存放第二个单词(用->实现)
(*tmp + 1)->key = (char *)malloc(strlen("b") + 1);
strcpy((*tmp + 1)->key, "b");
(*tmp + 1)->content = (char *)malloc(strlen("字母B") + 1);
strcpy((*tmp + 1)->content, "字母B");
//第三个结构体存放第三个单词
(*tmp + 2)->key = (char *)malloc(strlen("c") + 1);
strcpy((*tmp + 2)->key, "c");
(*tmp + 2)->content = (char*)malloc(strlen("字母C") + 1);
strcpy((*tmp + 2)->content, "字母C");
}
//把p传递到方法中,相当于操作struct dict *tmp = p
//此时等于把p指向的内容赋值给了tmp,tmp指针同样也指向
//p所指向的空间了,那么就可以通过tmp取读取空间中的内容
void print_dict(struct dict *tmp) {
int i = 0;
for (; i < MAX; i++) {
printf("[%s]%s\n", (tmp+i)->key, (tmp + i)->content);
}
}
void free_dice(struct dict *tmp) {
//先释放成员变量,再释放结构体指针变量
int i = 0;
for (i = 0; i < MAX; i++) {
if ((tmp + i)->key != NULL) {
free((tmp + i)->key);
(tmp + i)->key = NULL;
}
if ((tmp + i)->content != NULL) {
free((tmp + i)->content);
(tmp + i)->content = NULL;
}
}
}
int search_dict(struct dict *tmp, char *key, char *content) {
int i = 0;
for (i = 0; i < MAX; i++) {
if (strcmp(tmp[i].key,key) == 0)
{
strcpy(content, tmp[i].content);
return 1;
}
}
return 0;
}
int main(int argc, char *argv[]) {
//一个结构体存储一个单词
struct dict *p = NULL;
//给单词p分配空间,然后设置存储内容
get_dict(&p);
//遍历单词,上边分配空间时将p的地址传入,是为了修改p
//指向的内容,而下边是为了读取,不需要修改,所以直接传递p
print_dict(p);
//查询单词解释
char key[1024];
char content[1024];
while (1) {
printf("输入需要查询的单词:");
//注意这里使用fgets存在的问题,fgets接收到字符串保存到key
//中后,会在字符串末尾加上一个换行符\n,所以直接这样进行
//strcmp(key,"break")等比较的操作是行不通的,因为他多了个换行符
//除非这样比较strcmp(key,"break\n")
//fgets(key, sizeof(key), stdin);
scanf("%s", key);
if (strcmp(key,"break")==0)
{
break;
}
int flag = search_dict(p,key, content);
if (flag == 0)
{
printf("查询不到次单词%s\n", key);
}
else {
printf("%s 的解释为:%s\n",key, content);
}
}
//释放空间
void free_dice(p);
system("pause");
}