终于到了精髓的地方了,这确实有点懵,总感觉这太麻烦了,而且写着也不爽,还是怀念py或者java,但也没办法,还是要继续学下去。

一、运算符&

  1. scanf("%d" , &i); 里的&
  2. 获取变量的地址,它的操作数必须是变量
  3. 地址的大小是否与int相同取决于编译器
#include <stdio.h>
int main(void)
{
int i = 0;
printf("0x%x\n", &i);
// 0x62fe4c
return 0;
}

&不能取的地址

&不能对没有地址的东西取地址

  1. &(a+b) ?
  2. &(a++) ?

二、指针

  1. 如果能够将获得变量的地址传递个一个函数,能否通过这个地址在那个函数内访问这个变量?
  2. scanf("%d" , &i);
  3. scanf()的原型应该是怎样的?我们需要一个参数保存别的变量的地址,如何表达能够保存地址的变量

指针

就是保存地址的变量 , *p

// p是一个指针,现在把i的地址交给了p
int i;
int* p = &i; // 下面两种形式一样,p是一个指针,而q是一个普通的int变量
int* p , q;
int *p , q;

指针变量

  1. 变量的值是内存的地址
  2. 普通变量的值是实际的值
  3. 指针变量的值是具有实际值的变量的地址

作为参数的指针

  1. void f(int *p);
  2. 在被调用的时候得到了某个变量的地址
  3. int i = 0;f(&i);
  4. 在函数里面可以通过这个指针访问到外面的的这个i
#include <stdio.h>

void f(int *p);

int main(void)
{
int i = 6;
printf("&i=%p\n", &i);
f(&i); return 0;
} void f(int *p)
{
printf(" p=%p\n", p);
} // 可以看到这里获取的地址是相同的
// &i=000000000062FE4C
// p=000000000062FE4C

访问那个地址上的变量*

  1. *是一个单目运算符,用来访问指针的值所表示的地址上的变量
  2. 可以是右值也可以是左值
  3. int k = *p;
  4. *p = k + 1;

*左值之所以叫左值

  1. 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果
  2. a[0] = 2;
  3. *p = 3;
  4. 是特殊的值,所以叫左值
#include <stdio.h>

// 声明两个函数
void f(int *p);
void g(int k); int main(void)
{
int i = 6;
printf("&i=%p\n", &i);
f(&i); // 此时i的值已经发生了变化
g(i); return 0;
} // 传入的是地址
void f(int *p)
{
printf(" p=%p\n", p);
printf("*p=%d\n", *p);
*p = 66;
} // 传入的普通int
void g(int k){
printf("k=%d\n", k);
} // &i=000000000062FE4C
// p=000000000062FE4C
// *p=6
// k=66

三、指针的使用

指针应用场景一

交换两个变量的值

#include <stdio.h>

void swap(int *pa , int *pb);

int main()
{
int a = 5;
int b = 10;
swap(&a , &b);
printf("a=%d , b=%d\n", a , b);
return 0;
} void swap(int *pa , int *pb){
int t = *pa;
*pa = *pb;
*pb = t;
}

指针应用场景二

  1. 函数返回多个值,某些值就只能通过指针返回
  2. 传入的参数实际上是需要保存带回的结果的变量
#include <stdio.h>

void minmax(int a[] , int len , int *min , int *max);

int main(void)
{
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,15,34,35,66,};
int min , max;
minmax(a , sizeof(a)/sizeof(a[0]) , &min , &max);
printf("min = %d , max = %d \n", min , max); return 0;
} void minmax(int a[] , int len , int *min , int *max)
{
int i;
*min = *max = a[0];
for (i = 0; i < len; i++)
{
if (a[i] > *max)
{
*max = a[i];
}
if (a[i] < *min)
{
*min = a[i];
}
}
}

指针应用场景二b

  1. 函数返回运算的状态,结果通过指针返回
  2. 常用的套路是让函数返回特殊的不属于有效范围内的值表示出错
    • -1 或 0
  3. 但是当任何数值都是有效的可能结果时,就得分开返回了
#include <stdio.h>

// 如果成功就返回1,否则就是0
int divide(int a , int b , int *result); int main(void)
{
int a = 5;
int b = 2;
int c;
if (divide(a,b,&c))
{
printf("%d/%d = %d\n", a, b, c);
} return 0;
} int divide(int a , int b , int *result)
{
int ret = 1;
if ( b== 0)
{
ret = 0;
}else{
*result = a/b;
} return ret;
}

指针常见的错误

定义了指针变量,还没有指向任何变量,就开始使用了

四、指针与数组

传入函数的数组成什么了?

  1. 函数参数表中的数组实际上是指针
  2. sizeof(a) == sizeof(int*)
  3. 但是可以用数组的运算符[]进行运算

下面的四种函数原型是等价的

int sum(int *ar , int n);
int sum(int* , int);
int sum(int ar[] , int n);
int sum(int[] , int);

数组变量是特殊的指针

数组变量本身表达地址,所以

  1. int a[10]; int*p = a; // 无需用&取地址
  2. 但是数组的单元表达的是变量,需要用&取地址
  3. a == &a[0]

[]运算符可以对数组做,也可以对指针做

  1. p[0] <===> a[0]

*运算符可以对指针做,也可以对数组做

  1. *a = 25

数组变量是const的指针,所以不能被赋值

五、指针与const

  1. 表示一旦得到了某个变量的地址,不能再指向其他变量
	// q内写的地址不能被改变
int *const q = &i;
*q = 26; // ok
q++; // error
  1. 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)
	const int *p = &i;
*p = 26; // error (*p是不能变的)
i = 26; // ok
p = &j; // ok
  1. 看懂下面的意思?
	const int * p1 = &i;
int const * p2 = &i; int *const p3 = &i;

判断那个被const的标志是const在*的前面还是后面

前面两个*p不能被修改,就像第二种情况

const数组

const int a[] = {1,2,3,4,5,6,};
  1. 数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
  2. 所以必须通过初始化进行赋值

保护数组值

  1. 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
  2. 为了保护数组不被函数破坏,可以设置参数为const
int sum(const int a[] , int length);

六、指针运算

1+1 = 2 ?那么指针加1等于什么呢

#include <stdio.h>

int main(void)
{
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = ac; printf("p =%p\n", p);
printf("p + 1 =%p\n\n", p+1);
// p =000000000062FE30
// p + 1 =000000000062FE31
// 相差了1 int ai[] = {0,1,2,3,4,5,6,7,8,9,};
int *q = ai; printf("q =%p\n", q);
printf("q + 1 =%p\n\n", q+1);
//q =000000000062FE00
//q + 1 =000000000062FE04
// 相差了4 return 0;
}

我们不难发现,在char中,相差了1,在int中相差了4,这是怎么回事?

sizeof(char) = 1 , sizeof(int) = 4

对指针做一个加1的动作,意味着要把它移动到下一个单元去

int a[10];
int *p = a;
*(p+1) --> a[1]

如果指针不是指向一片连续分配的空间,如数组,那么这运算没有意义

可以对指针的算术运算

  1. 给指针加、减、一个整数(+、+=、-、-=)
  2. 递增递减(++/--)
  3. 两个指针相减
#include <stdio.h>

int main(void)
{
char ac[] = {0,1,2,3,4,20,6,7,8,9,};
char *p1 = &ac[5];
char *p2 = &ac[4]; printf("p1 - p2 = %d\n" , p1 - p2);
printf("*p1 - *p2 = %d\n" , *p1 - *p2);
// p1 - p2 = 1 也就是 5 -4
// *p1 - *p2 = 16 也就是 20 - 4 int ai[] = {0,1,2,3,4,5,6,7,8,18,};
int *q1 = &ai[9];
int *q2 = &ai[2]; printf("q2 - q1 = %d\n", q2 - q1);
printf("*q2 - *q1 = %d\n", *q2 - *q1);
// q2 - q1 = -7 也就是 2 - 9
// *q2 - *q1 = -16 也就是 2 - 18 return 0;
}

*p++

  1. 取出p所指的那个数据来,完事之后顺便把p移动到下一个位置
  2. *的优先级虽然高,但没有++高
  3. 常用于数组类的连续空间操作
  4. 在某些cpu上,这可以直接被翻译成一条汇编指令
	char ac[] = {0,1,2,3,4,20,6,7,8,9,-1};
char *p = &ac[0]; // 普通方法遍历数组
// int i ;
// for (i = 0; i < sizeof(ac)/sizeof(ac[0]); i++)
// {
// printf("%d\n", ac[i]);
// } // 普通指针遍历数组
// for (p = ac; *p != -1; p++)
// {
// printf("%d\n", *p);
// } // printf("---------------\n"); // 使用*p++,快
while(*p != -1){
printf("%d\n", *p++);
}

0地址

  1. 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
  2. 所以你的指针不应该具有0值
  3. 因此可以用0地址来表示特殊的事情
    • 返回的指针是无效的
    • 指针没有被真正的初始化(先初始化为0)
  4. NULL是一个预定定义的符号,表示0地址

指针的类型

  1. 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
  2. 但是指向不同类型的指针是不能直接互相赋值的
  3. 只是为了避免用错指针

用指针来做什么

  1. 需要传入较大的数据时用作参数
  2. 传入数组后对数组做操作
  3. 函数返回不止一个结果
  4. 需要用函数修改不止一个变量
  5. 动态申请内存时

七、动态内存分配

输入数据

  1. 如果输入数据时,先告诉你个数,然后再输入,要记录每个数据
  2. C99可以用变量做数组定义的大小,C99之前呢?
#include <stdio.h>
#include <stdlib.h> int main(void)
{
int number;
int *a;
int i;
printf("请输入数量:\n");
scanf("%d" , &number); // 动态内存分配用malloc,此时a就相当于数组
a = (int*)malloc(number*sizeof(int)); for (i = 0; i < number; i++)
{
scanf("%d" , &a[i]);
} for (i = number - 1; i >=0; i--)
{
printf("%d\n", a[i]);
} // 最后释放空间
free(a); return 0;
}

malloc函数

#include <stdlib.h>
void* malloc(size_t size);
  1. 向malloc申请的空间的大小是以字节为单位的,需要什么类型就在sizeof里面写什么类型
  2. 返回的结果是void* , 需要类型转换为自己需要的类型
  3. 最后释放空间
(int*)malloc(n*sizeof(int));

没空间了?

  1. 如果申请失败则返回0 , 或者叫做NULL
  2. 你的系统可以给你多大的空间?
#include <stdio.h>
#include <stdlib.h> int main(void)
{
void *p;
int cnt = 0; while((p = malloc(100 * 1024 *1024))){
cnt ++ ;
} printf("分配了%d00MB的空间\n", cnt); return 0;
}
// 分配了27200MB的空间

free()

  1. 把申请得来的空间还给系统
  2. 只能还申请的空间的首地址
  3. 一个malloc对应一个free

C语言入门-指针的更多相关文章

  1. Go语言入门系列(五)之指针和结构体的使用

    Go语言入门系列前面的文章: Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 1. 指针 如果你使用过C或C++,那你肯定对指针这个概念 ...

  2. 【C语言入门教程】7.3 结构体指针的定义和引用

    C 语言中指针的操作非常灵活,它也能指向结构体变量对结构体变量进行操作.在学习结构指针之前,需要再次加深对指针的认识.声明指针变量时所使用的数据类型修饰符实际上的作用是定义指针访问内存的范围,如果指针 ...

  3. C语言入门---第九章 C语言指针

    没学指针就是没学C语言! 指针是C语言的精华,也是C语言的难点. 所谓指针,也就是内存的地址,所谓指针变量,也就是保存了内存地址的变量.不过人们往往不会区分两者的概念,而是混淆在一起使用. ===== ...

  4. 我为什么反对推荐新人编程C/C++语言入门?

    虽然我接触编程以及计算机时间比较早,但是正式打算转入程序员这个行当差不多是大学第四年的事情 从03年接触计算机,07年开始接触计算机编程, 期间接触过的技术包括 缓冲区溢出(看高手写的shellcod ...

  5. c语言入门教程 / c语言入门经典书籍

    用C语言开始编写代码初级:C语言入门必备(以下两本书任选一本即可) C语言是作为从事实际编程工作的程序员的一种工具而出现的,本阶段的学习最主要的目的就是尽快掌握如何用c语言编写程序的技能.对c语言的数 ...

  6. 【转】c语言入门教程 / c语言入门经典书籍

    用C语言开始编写代码 初级:C语言入门必备 (以下两本书任选一本即可) C语言是作为从事实际编程工作的程序员的一种工具而出现的,本阶段的学习最主要的目的就是尽快掌握如何用c语言编写程序的技能.对c语言 ...

  7. C语言入门(1)——C语言概述

    1.程序与编程语言 我们使用计算机离不开程序,程序告诉计算机应该如何运行.程序(Program)是一个精确说明如何进行计算的指令序列.这里的计算可以是数学运算,比如通过一些数学公式求解,也可以是符号运 ...

  8. c语言入门

    c 语言现在是一门很流行的语言,它介于汇编语言和高级语言之间,我认为 它属于中级语言,如c语言 的指针 ,位操作符,等,因为接近于汇编语言,c语言的执行代码效率很高 现在大多数的系统 如unix,和l ...

  9. PBFT概念与Go语言入门(Tendermint基础)

    Tendermint作为当前最知名且实用的PBFT框架,网上资料并不很多,而实现Tendermint和以太坊的Go语言,由于相对小众,也存在资料匮乏和模糊错漏的问题.本文简单介绍PBFT概念和Go语言 ...

随机推荐

  1. unity编辑器扩展_06(给选项添加快捷键,控制菜单是否启用)

    代码: [MenuItem("Tools/Delete ", true, 1)]    static bool DeleteVadidate()    {        if (S ...

  2. Lock同步锁

    Lock同步锁 一.前言 在Java 5.0 之前,协调共享对象的访问时可以使用的机制只有synchronized 和volatile .Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁 ...

  3. MSIL实用指南-创建对象

    创建对象用Newobj指令,它的操作是创建一个新的对象或值类型,并将对象引用的新实例到计算堆栈上.格式是Newobj <构造函数>实例: ilGenerator.Emit(OpCodes. ...

  4. OpenCvSharp 通过特征点匹配图片

    现在的手游基本都是重复操作,一个动作要等好久,结束之后继续另一个动作.很麻烦,所以动起了自己写一个游戏辅助的心思. 这个辅助本身没什么难度,就是通过不断的截图,然后从这个截图中找出预先截好的能代表相应 ...

  5. 第三篇 视觉里程计(VO)的初始化过程以及openvslam中的相关实现详解

    视觉里程计(Visual Odometry, VO),通过使用相机提供的连续帧图像信息(以及局部地图,先不考虑)来估计相邻帧的相机运动,将这些相对运行转换为以第一帧为参考的位姿信息,就得到了相机载体( ...

  6. Angular Material 的设计之美

    前言 Angular Material 作为 Angular 的官方组件库,无论是设计交互还是易用性都有着极高的质量.正如官方所说其目的就是构建基于 Angular 和 Typescript 的高质量 ...

  7. Python数据分析之Pandas读写外部数据文件

    1 引言 数据分析.数据挖掘.可视化是Python的众多强项之一,但无论是这几项中的哪一项都必须以数据作为基础,数据通常都存储在外部文件中,例如txt.csv.excel.数据库.本篇中,我们来捋一捋 ...

  8. hdu6376 度度熊剪纸条 思维

    度度熊剪纸条 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...

  9. 【转 | 侵删】2D 绘图技术中的坐标系统与坐标变换

    本文介绍在 2D 绘图技术中的坐标系统和坐标变换的相关知识.同时介绍 Kity 在这方面提供的 API .希望这些知识对于需要进行图形应用开发的同学会有所帮助. 锤子的故事 很久以前,有一个画家,他很 ...

  10. lightoj 1248-G - Dice (III) (概率dp)

    题意:给你n个面的骰子,问扔出所有面的期望次数. 虽然这题挺简单的但还是要提一下.这题题目给出了解法. E(m)表示得到m个不同面的期望次数. E(m+1)=[((n-m)/n)*E(m)+1]+(m ...