终于到了精髓的地方了,这确实有点懵,总感觉这太麻烦了,而且写着也不爽,还是怀念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. egret之moveclip 序列帧动画

    废话不多说,直接上代码: private addEffect(name, x: number, y: number, act?: string) { let data = RES.getRes(nam ...

  2. Scratch 3下载,最新版Scratch下载,macOS、Windows版

    下载地址:https://scratch.mit.edu/download 废话不多说,先上下载地址! 之前小弟学习Scratch,用的2.0发现诸多BUG,到度娘想下最新版却没有发现一篇比较正经的文 ...

  3. electron-vue-webpack引入bootstrap多实例问题Multiple instances of Vue detected!

    在node modules里面找到electron-webpack目录, 修改out->main.js白名单内容,增加 whiteListedModules.add("bootstra ...

  4. xcode7中搭建python开发环境

    1. 双击打开Xcode 2. 点击File->New->New Project 3. 在左边的面板选择Other,右边选择External Build Sytem,点击Next 4. 输 ...

  5. 原来JS是这样的 - 对象属性

    引子 在上一篇(原来JS是这样的 (2))刚发布的时候就阅读了那篇文章的人可能会注意到那篇曾用过"JavaScript 中万物皆对象"的说法,而在随后我发现错误后立即更新改掉了这个 ...

  6. CSS动效集锦,视觉魔法的碰撞与融合(二)

    引言 长久以来,我认识到.CSS,是存在极限的.正如曾经替你扛下一切的那个男人,也总有他眼含热泪地拼上一切,却也无法帮你做到的事情,他只能困窘地让你看到他的无能为力,怅然若失. 然后和曾经他成长的时代 ...

  7. 解决homebrew下install时出现的问题

    在homebrew下install   influxdb时出现error: Error: Failure while executing: git config --local --replace-a ...

  8. idea中applicationContext-trans.xml中的Cannot resolve bean 'dataSource'...的问题解决

    问题如下: (applicationContext-trans.xml中的部分截图) 先了解问题是怎么出现的: 此处的dataSource是在applicationContext-dao.xml中配置 ...

  9. springmvc 事务控制与数据库隔离级别

    springmvc 事物传播与数据库隔离控制 http://www.cnblogs.com/yangy608/archive/2011/06/29/2093478.html 一.Propagation ...

  10. 利用maven命令将外部jar包导进maven仓库

    命令如下:mvn install:install-file -DgroupId=com.zebra -DartifactId=ZSDK_API -Dversion=v2.12.3782 -Dpacka ...