目录

  • 相关概念
  • 数组与函数
  • 栈空间和堆空间的差异
  • 指针常量与常量指针
  • 指针数组与数组指针
  • 二级指针
    • 二级指针的传递
    • 二级指针的偏移(索引式排序)

相关概念
  • 指针的大小,在32系统上是4个字节;在64位系统上,是8个字节。 (32位系统,32根地址线,寻址空间为232次方,最大地址值为232-1,只需4个字节即32位存储地址。)
void test3()
{//指针的大小,在32系统上是4个字节;在64位系统上,是8个字节
int number = 10;
int * p = &number;
printf("sizeof(p) = %d\n", sizeof(p));
}
  • 野指针:定义指针未初始化或者释放空间后指针未置NULL

    内存泄漏:申请空间后未释放(malloc/free)

    内存踩踏:使用了不属于某一个变量或者指针开辟出来的空间

  • 左值和右值

    lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。

    rvalue通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是在不在内存中占有确定位置的表达式。

数组与函数
  • 函数参数传递数组

    1. 一维数组长度无法传递。
    2. 一维数组作为参数,本质上传递的是指针(例子中只会打印一个数字!)
void test0(int arr[]) {
int *p = arr;
for (int idx = 0; idx != sizeof(arr) / sizeof(int); ++idx) {
printf("%2d", *p++);
}
printf("\n");
} int main() {
int arr[] = { 1,2,3,4,5 };
test0(arr); //传入的是指针
return 0;
}
  • sizeof() 函数:

一维数组打印

void test1()
{
int arr[] = { 1, 2, 3, 4, 5 };
int * p = arr;
for (int idx = 0; idx != sizeof(arr) / sizeof(int); ++idx) {
printf("%2d", *p++);
}
printf("\n");
}

二维数组打印

#include<stdio.h>
#include<string.h> void print(int(*p)[4], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
} } int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
print(arr, 3);
return 0;
}
栈空间和堆空间的差异
#include<stdio.h>
#include<string.h>
#include<stdlib.h> //函数栈空间释放后,函数内所有局部变量消失
char* print_stack() {
char c[] = "I am a print_stack.";
puts(c);
return c;
} //堆空间不会因函数执行结束而释放
char* print_malloc() {
char* p;
p = (char*)malloc(20);
strcpy(p, "I am a print_malloc.");
puts(p);
return p;
} int main() {
char* p;
p = print_stack(); //数据在栈空间
puts(p); //打印异常
p = print_malloc(); //数据在堆空间
puts(p); return 0;
}
指针常量与常量指针
  • 指针常量:不能改变指针的指向,只能改变指向的值。
    int arr[10] = { 1, 2, 3, 4, 5 };
int * const p = arr; //指针常量(const pointer)
int number = 10;
//p = &number; //error 不能改变指针的指向
*p = 10;//ok 可以改变指针所指变量的值
  • 常量指针:指向常量的指针,不能改变指针指向的值,只能改变指针的指向
    const int * p1 = &number;	//常量指针(pointer to const)
p1 = arr; //p1可以改变指向
//*p1 = 10;//error 不能修改p1所指空间的内容

常量指针的另一种写法 int const * p1 = &number;

指针数组与数组指针
  • 定义
	int (*p1)[5];//指向一个大小为5的数组,数组里面存放的是整型数据
int * p2[5];//指针数组
  • &arr和arr的地址相同,不同的是arr+0是int型指针,而&arr是数组指针。

  • &+数组名的使用

void test1()
{
int arr[] = { 1, 2, 3, 4, 5 };
int * p = arr;
p = &arr;
printf("%d\n", *(&arr + 1)); //&arr 代表的是数组指针类型,越界
printf("%d\n", *(arr + 1)); //打印2
printf("*p = %d\n", *p); //打印1
}

其中*(&arr+1)偏移了整个数组的大小,解引用后越界!同理如果是二维数组也会偏移整个二维数组的大小。

  • 数组指针的应用

“二维数组的实现是通过二级指针实现的”这是错误的!二维数组通过两次偏移到获取到数组中的某一个元素,所使用的指针是数组指针,数组指针是一级指针

#include<stdio.h>
#include<string.h> void print(int(*p)[4], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
}
} //数组指针用于二维数组的传递和偏移
int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
int b[4] = { 1,2,3,4 };
int i = 10;
int(*p)[4];//定义一个数组指针
p = arr;
print(p, 3); return 0;
}

强转实现4行3列输出

#include<stdio.h>
#include<string.h> void print(int(*p)[3], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
}
} //数组指针用于二维数组的传递和偏移
typedef int(*ptype)[3];
int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
int b[4] = { 1,2,3,4 };
int i = 10;
int(*p)[3];//定义一个数组指针
print((ptype)arr, 4); return 0;
}
二级指针
  1. 二级指针的传递
  • 下 面 有 一 个 实 例 , 整 型 指 针 pi, 指 向 整 型 变 量 i, 整 型 指 针 pj 指 向 整 型 变 量 j , 通 过 子

    函 数 change, 我 们 想 改 变 指 针 变 量 pi 的 值 , 让 其 指 向 j, 我 们 知 道 c 语 言 的 函 数 调 用 是 值 传

    递 , 因 此 要 想 在 change 中 改 变 变 量 pi 的 值 , 那 么 必 须 把 pi 的 地 址 传 递 给 change,pi是一级

    指针,&pi的类型即为2级指针。

  • 要 想 在 子 函 数 改 变 一 个 变 量 的 值 , 必 须 把 该 变 量 的 地 址 传 进 去

  • 要 想 在 子 函 数 改 变 一 个 指 针 变 量 的 值 , 必 须 把 该 指 针 变 量 的 地 址 传 进 去,二 级 指 针 传 递

  • 对 于 二 级 指 针 的 传 递 使 用 场 景 , 把 握 两 点 , 第 一 . 二 级 指 针 变 量 定 义 是 在 形 参 , 第 二

    在 调 用 函 数 中 往 往 不 定 义 二 级 指 针 , 如 果 定 义 , 初 始 化 注 意 是 一 级 指 针 的 取 地 址 。

#include<stdio.h>
#include<stdlib.h> void change(int **p2, int *pb) {
*p2 = pb;
} //要 想 在 子 函 数 改 变 一 个 变 量 的 值 , 必 须 把 该 变 量 的 地 址 传 进 去
//要 想 在 子 函 数 改 变 一 个 指 针 变 量 的 值 , 必 须 把 该 指 针 变 量 的 地 址 传 进 去
int main() {
int i = 10;
int j = 5;
int *pi, *pj;
pi = &i;
pj = &j;
printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj); //10 5 10 5
change(&pi, pj);
printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj); //10 5 5 5
return 0;
}
  1. 二级指针的偏移

一 级 指 针 的 偏 移 服 务 于 数 组 , 整 型 一 级 指 针 服 务 于 整 型 数 组 , 所 以 二 级 指 针 的 偏 移 也 服

务 于 数 组 , 服 务 对 象 为 指 针 数 组

  • 应用:索引式排序
#include<stdio.h>
#include<string.h> //void print(char *p[], int len) 二级指针的偏移服务的是指针数组
void print(char **p, int len) { //二级指针每次偏移4个字节(因为一级指针在32位系统中占4个字节)
int i;
for (i = 0;i < len;++i) {
puts(p[i]);
}
} void printArr(char(*p)[10], int row) {
int i;
for (i = 0;i < row;++i) {
puts(p[i]);
}
} int main() {
char b[5][10] = { "php","python","linux","unix","java" };
char *p[5]; //指针数组
int i, j;
char *tmp;
for (i = 0;i < 5;++i) { //让指针数组中的每一个指针指向一个字符串
p[i] = b[i];
}
print(p, 5);
printf("--------------------\n");
printArr(b, 5);
printf("--------------------\n");
for (int i = 4;i > 0;--i) {
for (int j = 0;j < i;++j) {
if (strcmp(p[j], p[j + 1]) > 0) {
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
print(p, 5); return 0;
}

主函数内定义二级指针实现:

#include<stdio.h>
#include<string.h> //void print(char *p[], int len) 二级指针的偏移服务的是指针数组
void print(char **p, int len) { //二级指针每次偏移4个字节(因为一级指针在32位系统中占4个字节)
int i;
for (i = 0;i < len;++i) {
puts(p[i]);
}
} void printArr(char(*p)[10], int row) {
int i;
for (i = 0;i < row;++i) {
puts(p[i]);
}
} int main() {
char b[5][10] = { "php","python","linux","unix","java" };
char **p; //等价于char *p[];
int i, j;
p = (char**)malloc(20); //一级指针强制类型转换为二级指针,使用动态的指针数组
char *tmp;
for (i = 0;i < 5;++i) { //让指针数组中的每一个指针指向一个字符串
p[i] = b[i];
} print(p, 5);
printf("--------------------\n");
printArr(b, 5);
printf("--------------------\n");
for (int i = 4;i > 0;--i) {
for (int j = 0;j < i;++j) {
if (strcmp(p[j], p[j + 1]) > 0) {
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
print(p, 5); return 0;
}
  • 传递二级指针修改指针本身的值
#include <stdio.h>

//传递二级指针修改指针本身的值
void swap(int ** p1, int ** p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
printf("函数内:*p1 = %d, *p2 = %d\n", **p1, **p2);
} void test6()
{
int number1 = 3, number2 = 4;
int * p1 = &number1;
int * p2 = &number2; printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
swap(&p1, &p2);
printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
} int main(void)
{
test6();
return 0;
}

C语言强化——指针的更多相关文章

  1. 浅谈c语言的指针

    对于非计算机专业的同学,c语言的指针往往就是老师的一句“指针不考“就带过了.c语言的指针号称是c语言的灵魂,是c语言中最精妙的部分. 指针本质上也是变量,也就是一段内存,只是他的特殊之处是他存储的数据 ...

  2. C#委托与C语言函数指针及函数指针数组

    C#委托与C语言函数指针及函数指针数组 在使用C#时总会为委托而感到疑惑,但现在总新温习了一遍C语言后,才真正理解的委托. 其实委托就类似于C/C++里的函数指针,在函数传参时传递的是函数指针,在调用 ...

  3. C语言二重指针与malloc

    (内容主要源于网上,只是加入了些自己的剖析) 假设有一个二重指针: char **p; 同时有一个指针数组 char *name[4]; 如何引用p呢? 首先我们有程序代码如下 #include &l ...

  4. C语言函数指针基础

    本文写的非常详细,因为我想为初学者建立一个意识模型,来帮助他们理解函数指针的语法和基础.如果你不讨厌事无巨细,请尽情阅读吧. 函数指针虽然在语法上让人有些迷惑,但不失为一种有趣而强大的工具.本文将从C ...

  5. 为什么C/C++语言使用指针

    这是参加面试时,面试官问的一道开放性题目. 问题是:为什么C/C++语言使用指针? 这个问题一问出来,直接被面试官秒杀了,面试官大神,你怎么不按套路出牌啊? 说好的malloc和new的区别呢?说好的 ...

  6. C语言的指针变量

    C语言的指针变量 在C语言中,变量是固定范围的存储空间,它存储的是赋给他的值, 比如: ; /* 这里是定义一个整型变量a,并把12这个值存储在a的地址空间上 这个地址空间是系统随机分配的,对用户是透 ...

  7. Android For JNI(五)——C语言多级指针,结构体,联合体,枚举,自定义类型

    Android For JNI(五)--C语言多级指针,结构体,联合体,枚举,自定义类型 我们的C已经渐渐的步入正轨了,基础过去之后,就是我们的NDK和JNI实战了 一.多级指针 指针的概念我们在前面 ...

  8. “对外部(局部)变量的访问”是C语言函数指针的最大弱点

    1.“对外部(局部)变量的访问”是C语言函数指针的最大弱点 . #include <stdio.h> #include <stdlib.h> /* 结构体定义 */ struc ...

  9. go语言学习--指针的理解

    Go 的原生数据类型可以分为基本类型和高级类型,基本类型主要包含 string, bool, int 及 float 系列,高级类型包含 struct,array/slice,map,chan, fu ...

随机推荐

  1. python 字符串,列表,元组,字典相互转换

    1.字典 dict = {'name': 'Zara', 'age': 7, 'class': 'First'} 字典转为字符串,返回:<type 'str'> {'age': 7, 'n ...

  2. LG4781 【模板】拉格朗日插值

    题意 题目描述 由小学知识可知,$n$个点$(x_i,y_i)$可以唯一地确定一个多项式 现在,给定$n$个点,请你确定这个多项式,并将$k$代入求值 求出的值对$998244353$取模 输入输出格 ...

  3. 【SpringBoot】SpringBoot热部署和配置文件自动注入实战

    ========================3.SpringBoot热部署devtool和配置文件自动注入实战 ============================ 1.SpringBoot2 ...

  4. Cassandra基础3

    cassandra读性能优化:1.禁用read repair每次读操作,无论读请求设置读一个节点还是多个节点,cassandra返回给客户端最新的数据后,都会后台对比所有副本的数据并对差异数据进行修复 ...

  5. Scala中的Map使用例子

    Map结构是一种非常常见的结构,在各种程序语言都有对应的api,由于Spark的底层语言是Scala,所以有必要来了解下Scala中的Map使用方法. (1)不可变Map特点: api不太丰富 如果是 ...

  6. 你不知道的JavaScript(中卷) (Kyle Simpson 著)

    第一部分 类型和语法 第1章 类型 1.1 类型 1.2 内置类型 1.3 值和类型 1.3.1 undefined和undeclared 1.3.2 typeof Undeclared 1.4 小结 ...

  7. LoadRunner手写脚本、检查点、集合点、事务、思考时间

    手写脚本 什么时候要手写? 可以有条件手写脚本的场景有两类: 有接口说明文档 没有借口说明文档,要去录制,录制不了,抓包手写 所需函数 我们这里讲的例子是基于 http 协议的,也是常见的两种请求类型 ...

  8. day10 内容大纲

    01 去年内容回顾 01 去年内容回顾 *args **kwargs: 万能参数,动态参数 * 魔性用法: 函数的定义时,* ** 聚合. 函数的执行时,* ** 打散. 形参顺序: 位置参数,*ar ...

  9. TypeScript 之 书写.d.ts文件

    https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/Writing%20Definition%20Files.html ...

  10. 怎么理解Python画图中的X,y

    X_outliers=np.array([[3.4, 1.3], [3.2, 0.8]]) y_outliers=np.array([0, 0]) 要明白X,y不再是我们高中时候学的x,y轴的坐标:首 ...