C语言指针
- 为什么使用指针?
- 指针的定义
- 指向数组的指针
- 指针的访问
- 访问指针所指向的内容
- 空指针
-
-
- 1、空指针
- 2、空指针的使用
- 坏指针
-
- 指针的算数运算(指针的自增、指针的自减、指针与整数的加减)
-
-
- 指针自加
- 指针自减
- 指针与整数加减
- 指针与指针之间的加减运算
-
- const 修饰
- 二级指针
- 函数指针
-
-
- 函数二级指针
- 数组和指针的纠缠
-
- 指针数组
-
-
- 指针和多维数组
-
- void类型的指针
- 函数指针
- 函数返回值
任何类型指针所占空间是4个字节,在计算机架构中最大的寻址范围为4个字节
为什么使用指针?
前言: 不使用指针也可以构建起软件,带着疑问往下看
- 函数的值传递,无法通过调用函数,来修改函数的实参
- 被调用函数需要提供更多的“返回值”给调用函数
- 减少值传递时带来的额外开销,提高代码执行效率
指针的定义
(跑了聊和尚跑不了庙:和尚是跑了但是,我们知道寺庙的地址,还是可以去寺庙找到那个和尚)
(买东西寄快递,只要给个地址,就可以收到快递)
#include <stdio.h>
#include <Windows.h>
int main( void )
{
int age;
/*
** 定义了一个指针
** 指针本身也是一个变量
** 名称是p,他是一个指针,可以指向一个整数
** p的值是一个整数的地址
*/
int *p;
/*
** p指向了age
** p的值就是age的地址
*/
p = &age;
/*
scanf_s("%d", &age);
*/
scanf_s("%d", p);
printf("age:%d \n", age);
system("pause");
return 0;
}

#include <stdio.h>
#include <time.h>
#include <Windows.h>
/*************************************************
** 1、函数的值传递,无法通过调用函数,来修改函数的实参
** 2、被调用函数需要提供更多的“返回值”给调用函数
** 3、减少值传递时带来的额外开销,提高代码执行效率
**************************************************/
/*给英雄加血的函数*/
void add_blood1( int blood )
{
blood += 1000;
}
bool add_blood3( int blood )
{
if ( blood >= 1000 )
{
return false;
}
else
{
blood+=1000;
}
return true;
}
int add_blood2( int blood )
{
blood += 1000;
return blood;
}
/*减少值传递时带来的额外开销,提高代码执行效率*/
struct _hexo_stat
{
int blood; //英雄血量
int power; //英雄攻击力
int level; //英雄级别
char name[64]; //英雄名字
char details[1024]; //描述英雄状态
};
struct _hexo_stat upgrade1(struct _hexo_stat hexo, int type)
{
switch(type)
{
case 1:
hexo.blood += 1000;
hexo.power += 30;
hexo.level++;
break;
case 2:
hexo.blood += 2000;
hexo.power += 50;
hexo.level++;
break;
default:
break;
}
return (hexo);
}
/*指针版本*/
void upgrade2(struct _hexo_stat *hexo, int type)
{
switch(type)
{
case 1:
hexo->blood += 1000;
hexo->power += 30;
hexo->level++;
break;
case 2:
hexo->blood += 2000;
hexo->power += 50;
hexo->level++;
break;
default:
break;
}
}
int main( void )
{
time_t start, end;
struct _hexo_stat hexo_xiaohei;
strcpy(hexo_xiaohei.name, "小黑");
hexo_xiaohei.blood = 100;
hexo_xiaohei.power = 10;
hexo_xiaohei.level = 1000;
time(&start); /*1970年1月1日0时0分0秒 至今的秒数*/
for(int i=0; i < 99999999; i++)
{
/*
upgrade1(hexo_xiaohei, 1);
*/
upgrade2(&hexo_xiaohei, 2);
}
time(&end);
printf("小黑的血量:%d \n", hexo_xiaohei.power);
printf("%d \n", end-start);
/*英雄小黑本身的血量*/
//int hexo_xiaohei = 1000;
/*加血*/
//add_blood1(hexo_xiaohei);
//hexo_xiaohei = add_blood3(hexo_xiaohei);
/*加血后的结果*/
/*
printf("hexo_xiaohei is blood :%d \n", hexo_xiaohei);
*/
system("pause");
return 0;
}
指向数组的指针
指针的初始化
#include <stdio.h>
#include <Windows.h>
int main( void )
{
int room = 900;
int *p1 = &room1;
int *p2 = &room1;
printf("room1的地址是:0x%p \n",&room);
printf("p1的地址是:0x%p \n",&p1);
printf("p2的地址是:0x%p \n",&p2);
/*
** 整型指针,所占字节数,在32位的系统里 寻址是 4个字节数
** 在64位的系统里 寻址是 8个字节数
*/
printf("room1所占字节是:%d \n",sizeof(room));
printf("p1所占字节是:%d \n",sizeof(p1));
printf("p2所占字节是:%d \n",sizeof(p2));
system("pause");
return 0;
}
指针的访问
访问指针
#include <stdio.h>
#include <Windows.h>
int main(void)
{
/*
** 指针也是一个变量
** 1. 访问指针本身
** 2.
** 访问(读、写)指针本身的值,和其他普通变量的访问方式相同
**
**
*/
int room = 2;
int *p1 = &room;
int *p2 = p1;
printf("room的地址:%d\n",&room);
printf("p1的值:%d p2的值:%d\n", p1, p2);
int *p3 = p1;
printf("p3的值:%d\n",p3);
p3 = &room;
printf("p3的值:%d room的地址:%d \n", p3, &room);
/*
** 使用16进制打印,把地址当成一个无符号数来处理
**
*/
printf("p1=0x%p\n", p1);
printf("p1=0x%x\n", p1);
printf("p1=0x%X\n", p1);
system("pause");
return 0;
}
访问指针所指向的内容
1、间接访问符(解引符)“ * ” 是一个特殊的运算符,girl表示读取指针girl所指向的变量
** 2、带个 “*” 就等同于间接地访问这个地址指向变量的值(的内容)*
#include <stdio.h>
#include <Windows.h>
int main(void)
{
/*
** 指针也是一个变量
** 1. 访问指针本身
** 2.
** 访问(读、写)指针本身的值,和其他普通变量的访问方式相同
**
**
*/
int room = 2;
int * girl = &room;
room = 3;
int x;
x = *girl; /* 间接访问符(解引符)“ * ” 是一个特殊的运算符,*girl表示读取指针girl所指向的变量*/
printf("x:%d\n",x);
*girl = 4;
printf("room:%d *girl:%d\n", room, *girl);
/* 带个 "*" 就等同于间接地访问这个地址指向变量的值(的内容)*/
system("pause");
return 0;
}
空指针
1、空指针
空指针就是值为,0 的指针。(任何数据都不会储存在地址为0的内存中,它是操作系统预留的内存块)
int p = 0;
或者
int p = NULL; //建议使用这种
2、空指针的使用
1、指针初始化为空指针
int *select = NULL;
目的是避免数据非法访问
2、指针不再使用时,可以设置为空指针
int *select = &pointer;
//不再使用了
select = NULL;
3、表示这个指针还没有具体的指向,使用前进行合法性的判断
#include <stdio.h>
#include <Windows.h>
int main(void)
{
//int *p = NULL;
//if(p){ //p等同于p!=NULL
//指针不为空,对指针进行操作
//}
int *p = NULL;
int pointer_p;
p = &pointer_p;
if(p!=NULL)
{
printf(" *p的值不为空 \n");
}
system("pause");
return 0;
}
坏指针
int *select; //指针没有初始化
形式一
printf(“选择的房间是:%d\n”, *select);
形式二
select = 100;
printf(“选择的房间是:%d\n”,select);
指针的算数运算(指针的自增、指针的自减、指针与整数的加减)
指针自加
#include <stdio.h>
#include <Windows.h>
int main(void)
{
int age[] = {21, 15, 18, 14, 23, 28, 10};
int len = sizeof(age)/sizeof(age[0]); /*计算age数组的长度*/
for( int i = 0; i < len; i++ )
{
printf("第%d学员的年龄是:%d\n", i+1, age[i]);
}
/*
** 数组本身的地址和数组第一个元素的地址是相同的
** 1、打印数组的地址
** 2、打印数组第一个元素的地址
*/
printf("age的地址:%d age[0]第一个元素的地址:%p\n",age, &age[0]);
int *p = age;
/*访问第一个元素*/
printf("数组的第一个元素:%d\n", *p);
/*访问第二个元素*/
//p = p+1; /* p = p + 1*(sizeof(int)) */
//printf("数组的第二个元素:%d p的地址是:0x%p\n", *p, p);
for( int i = 0; i < len; i++ )
{
printf("第%d学院的年龄:%d 地址:0x%p \n",i+1, *p, p);
p++;
}
printf("---------------------------\n");
char ch[4] = {'a', 'b', 'c', 'd'};
int len_ch = sizeof(ch)/sizeof(char);
char *cp = ch;
for(int i = 0; i < len_ch; i++)
{
printf("第%d个元素的值:%c 地址是:0x%p\n",
i+1, *cp, cp);
cp++;
}
system("pause");
return 0;
}
指针自减
输入字符串,逆转输出
#include <stdio.h>
#include <string>
#include <Windows.h>
/*
** 输入字符串进行,逆转输出
*/
int main(void)
{
char input[128];
int len;
char tmp;
scanf_s("%s", input, 128);
len = strlen(input);
for( int i = 0; i < len/2; i++)
{
tmp = input[0];
input[i] = input[len-i-1];
input[len-i-1] = tmp;
}
/*第一种方法*/
for( int i = 0; i < len; i++)
{
printf("%c", input[i]);
}
printf("\n");
printf("逆转后:%s", input);
/*第二种方法*/
for( int i = 0; i < len; i++)
{
printf("%c", input[len-i-1]);
}
printf("\n");
/*第三种方法*/
char *p = &input[len-1];
for( int i = 0; i < len; i++)
{
printf("%c", *p--);
//p--;
}
printf("\n");
system("pause");
return 0;
}
指针与整数加减
指针与整数的加减在数组中,在数组元素中表示元素向前移动一位或向后移动一位(数据类型 (int) p基地址 + n * sizeof(数据类型) )
#include <stdio.h>
#include <string>
#include <Windows.h>
int main(void)
{
int ages[] = {10,2,3,4,5,61,90,8,9,10};
int len = sizeof(ages) / sizeof(ages[0]);
int *p1 = ages;
printf("第一个人的年龄:%d\n", *p1 + 6); /* 优先级(*p)+6 */
printf("第二个人的年龄:%d\n", *(p1 + 6));
int *p2 = &ages[4];
printf("第二个人前一个人的年龄:%d\n",*(p2 - 1));
printf("第二个人前三个人的年龄:%d\n",*(p2 - 3));
system("pause");
return 0;
}
指针与指针之间的加减运算
指针与指针之间不能进行相加操作?
#include <stdio.h>
#include <string>
#include <Windows.h>
int main(void)
{
int ages[] = {10,2,3,4,5,6,4,8,9,10};
int len = sizeof(ages) / sizeof(ages[0]);
int *xiao_cheng = ages+6;
int *xiao_zhang = ages+7;
printf("xiao_zhang - xiao_cheng = %d\n",
*xiao_zhang - *xiao_cheng);
printf("xiao_zhang - xiao_cheng = %d\n",
xiao_zhang - xiao_cheng);
/*//指针之间不能相加
printf("xiao_zhang + xiao_cheng = %d\n",
xiao_zhang + xiao_cheng);
*/
system("pause");
return 0;
}
const 修饰
const 关键字 只读。const 离类型(int) 近,还是离指针变量名近,离谁近,就修饰谁,谁就不能变
#include <stdio.h>
#include <string>
#include <Windows.h>
/*
** const 关键字 只读
** const 离类型(int) 近,还是离指针变量名近,离谁近,就修饰谁,谁就不能变
*/
int main(void)
{
int wife = 24;
int girl = 18;
/*
** 渣男 随心所欲 没有什么约束
*/
int * zha_man = &wife;
* zha_man = 29;
zha_man = &girl;
* zha_man = 19;
printf("wife:%d girl:%d\n", wife, girl);
/*
** 直男 以自我为中心
*/
/* const修饰后 使用指针访问时 只能读 不能修改 */
/* const int * zhi_man = &wife; */ //第一种 写法
int const * zhi_man = &wife; //第二种 写法
/*
//不能给常量赋值
* zhi_man = 20;
*/
zhi_man = &girl;
printf("wife:%d girl:%d\n", wife, girl);
/*
** 暖男 终此一生
*/
int * const nuan_man = &wife;
/* nuan_man = &girl; */
printf("wife: %d\n",wife);
/*
** 超级暖男 一切宜顺老婆 听老婆的
*/
//既不能修改 所指向的 地址,也不能修改,里面的值
const int * const super_nuan_man = &wife;
printf("wife:%d\n",wife);
system("pause");
return 0;
}
二级指针
一级指针:里保存的是普通变量的地址
二级指针:里保存的是另一个指针变量的地址
#include <stdio.h>
#include <Windows.h>
int main(void)
{
int guizi2 = 888; //存手枪的第二个柜子
int * guizi1 = &guizi2; //存第二个柜子地址的 第一个柜子
int ** liu_jian = &guizi1;//手握第一个柜子地址的 刘健
printf("刘健手握第一个柜子的地址:0x%p\n",*liu_jian);
printf("刘健从第一个柜子拿到第二个柜子的地址:0x%p\n",&guizi2);
int *tmp;
tmp = &guizi2;
//printf("刘健从第一个柜子拿到第二个柜子的地址,打开柜子取出枪:%d\n",*(*liu_jian));
printf("刘健从第一个柜子拿到第二个柜子的地址,打开柜子取出枪:%d\n",*tmp);
system("pause");
return 0;
}
函数指针
#include <stdio.h>
#include <Windows.h>
/*一级指针*/
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int x = 100, y = 1000;
swap(&x , &y);
printf("x=%d ,y=%d\n",x,y);
system("pause");
return 0;
}
函数二级指针
二级指针可以将 参数 带入函数 ,也可以将函数 里的参数带出
#include <stdio.h>
#include <Windows.h>
void boy_home(int **meipo) //局部变量,函数结束,一起销毁
{
static int boy = 23;
*meipo = &boy;
}
int main(void)
{
int *meipo = NULL;
boy_home(&meipo); //main里的 *meipo 0x666
printf("boy: %d\n",*meipo);
system("pause");
return 0;
}
数组和指针的纠缠
#include <stdio.h>
#include <Windows.h>
//数组标表示
void array_pointer1(int days[], int len)
{
printf("====================================== \n");
for (int i = 0; i < len; i++)
{
printf("第 %2d 月的最后一天是 %d \n", i + 1, days[i]);
}
}
//指针表示
void array_pointer2(int days[], int len)
{
printf("====================================== \n");
for (int i = 0; i < len; i++)
{
printf("第 %2d 月的最后一天是 %d \n", i + 1, *(days + i));
}
}
int main(void)
{
int days[12] = {31, 28, 32, 30, 31, 28, 31, 31, 28, 31, 31, 28};
int len = sizeof(days) / sizeof(days[0]);
/*
for (int index = 0; index < len; index++)
{
printf("第 %2d 个月的最后一天是 %d \n", index+1, *(days+index));
}
*/
array_pointer1(days, LEN);
array_pointer2(days, LEN);
system("pause");
return 0;
}
指针数组
#include <stdio.h>
#include <Windows.h>
int main(void)
{
int girls[4][3] = { {178, 179, 167},
{135, 167, 157},
{176, 166, 190},
{155, 165, 180}};
int *qishou[2]; //定义一个有两个元素的指针数组,每个元素都是指针变量
if (girls[0][0] > girls[0][1]) // 如果 178 > 179
{
qishou[0] = &girls[0][0]; //把 第零个元素(178)地址 给 第一个旗手
qishou[1] = &girls[0][1]; //把 第一个元素(179)地址 给 第二个旗手
}
else //否则
{
qishou[0] = &girls[0][1]; //把 第一个元素(179)地址 给 第一个旗手
qishou[1] = &girls[0][0]; //把 第零个元素(178)地址 给 第二个旗手
}
for (int index = 2; index < 12; index++) //
{
if (*qishou[1] >= girls[index / 3][index % 3]) //如果 179 大于 girls[0][2]
{
continue;
}
if (girls[index / 3][index % 3] <= *qishou[0]) // 如果176 小于 178 就把 176 给 棋手 二
{
qishou[1] = &girls[index / 3][index % 3];
}
else //否则 就把 178 给棋手二 把 179 给棋手一
{
qishou[1] = qishou[0];
qishou[0] = &girls[index / 3][index % 3];
}
}
printf("最高的是:%d , 次高的是:%d \n", *qishou[0], *qishou[1]);
system("pause");
return 0;
}
指针和多维数组
#include <stdio.h>
#include <Windows.h>
int main(void)
{
int A_302[4][3] = { {1, 2, 3},
{4, 5, 6},
{7, 18, 9},
{10, 11, 12}};
int (*p)[3];
int* boy = NULL;
p = &A_302[0];
/*for (int i = 0;i < 4;i++)
{
for (int j = 0;j < 3;j++)
{
printf("%d ", (*p)[j]);
}
printf("\n");
p++;
}*/
boy = &(*p)[0];
for (int i = 0;i < 4;i++)
{
for (int j = 0;j < 3;j++)
{
printf("%d ", *((*p)+j));
if (*boy < *((*p) + j))
{
boy = (*p) + j;
}
}
printf("\n");
p++;
}
printf("偷窥的人是:%d \n", *boy);
return 0;
}
void类型的指针
所有其它类型的指针都可以隐式自动转换成void类型
#include <stdio.h>
#include <Windows.h>
int main(void)
{
int arr[] = {1, 2, 3, 4, 5};
char ch = 'a';
void * p = arr; //定义了一个void类型的指针
//p++; //void * 指针不允许进行算术运算
p = &ch; //其它类型都可以自动转换成void * 指针
//所有其它类型的指针都可以隐式自动转换成void类型
printf("p:0x%p \nch:0x%p \n",p ,&ch);
//强制类型转换
char * p1 = (char *)p;
printf("%c \n", *p1);
system("pause");
return 0;
}
函数指针
#include <stdio.h>
#include <Windows.h>
int compare_int(const void * a, const void * b)
{
printf("已经调用了 compare_int 哦\n");
int * p1 = (int *)a;
int * p2 = (int *)b;
//printf("a的地址:0x%p \nb的地址:0x%p\n",&a,&b);
return *p1 - *p2;
}
int compare_char(const void * a, const void * b)
{
printf("已经调用了 compare_int 哦\n");
char ch1 = *((char *) a);
char ch2 = *((char *) b);
if(ch1 >= 'A' && ch1 <= 'Z')
{
ch1 += 32;
}
if(ch2 >= 'A' && ch2 <= 'Z')
{
ch2 += 32;
}
//printf("a的地址:0x%p \nb的地址:0x%p\n",&a,&b);
return ch1 - ch2;
}
int main1(void)
{
int x = 10;
int y = 5;
//函数指针的定义 把函数声明移过来,把函数名改成(* 函数指针名)
int (*fp)(const void *, const void *);
fp = &compare_int;
(*fp)(&x,&y);
fp(&x,&y);
printf("compare_int 的地址:0x%p\n",&compare_int);
compare_int(&x, &y);
system("pause");
return 0;
}
int main(void)
{
//对整形数组排序
int arr1[] = {2, 5, 1, 22, 2124, 2321, 3, 9, 10};
qsort(arr1, sizeof(arr1)/sizeof(int), sizeof(int), &compare_int);
for(int i = 0; i < sizeof(arr1)/sizeof(int);i++)
{
printf("%d \n", arr1[i]);
}
//qsort 可以对任何类型的数组进行排序
char arr2[] = {"abcdefgABCDEFGHI"};
qsort(arr2, sizeof(arr2)/sizeof(char)-1, sizeof(char), &compare_char);
for(int i = 0; i < sizeof(arr2)/sizeof(char)-1;i++)
{
printf(" %c ",arr2[i]);
}
printf("\n");
system("pause");
return 0;
}
函数返回值
#include <stdio.h>
#include <Windows.h>
#include <iostream>
using namespace std;
int add(int x, int y)
{
int sum = x + y;
return sum;
}
int * add1(int x, int y)
{
int sum = x + y;
return ∑
}
int * add2(int x, int y)
{
int * sum = NULL;
sum = new int;
*sum = x + y;
return sum;
}
int * add3(int x, int y)
{
static int sum = 0;
cout << "sum:%d"<< sum <<endl;
sum = x + y;
return ∑
}
int main(void)
{
int a = 3;
int b = 5;
int * sum = NULL;
//sum = add1(a,b);
//add2(a,b);
//cout << add1(a,b) << endl;
//cout << *sum << endl;
//cout << *(add2(a,b)) << endl;
//delete sum;
//不能使用外部函数局部变量的地址
sum = add3(a,b);
cout << *sum << endl;
* sum = 888;
add3(a,b);
system("pause");
return 0;
}