一.指针的相关概念

1.1 指针变量

指针是一个变量,存放的是一个地址,该地址指向一块内存空间。

例:

int a = ;

int *p = &a; // 定义一个指针变量p,&符号可以取得一个变量在内存当前中的地址。

*p = ; // 修改指针所指的内存数据为5

1.2 无类型指针

定义一个指针变量,但不指定它指向具体哪种数据类型,可以通过强制转化将void *转化为其它类型指针,也可以用(void *)将其它类型指针强制转化为void *类型指针。

1.3 NULL

NULL在C语言中的定义为(void *)0,空指针就是指向了NULL的指针变量。

int *p = &a;
p = NULL; // p就是一个空指针

1.4 野指针

野指针就是没有指向任何有效地址的指针变量。在代码中应当尽量避免出现野指针,如果一个指针不能确定指向任何一个变量的地址,那么请将这个指针变成空指针。

二.使用指针时的一些注意点

2.1 指针的兼容性

指针之前赋值比普通数据类型赋值检查更为严格,如:不可以将一个double * 赋值给一个int。且指针变量只能存放地址,也不能将一个int类型变量直接赋值给一个指针。

2.2 常量指针与指针常量

记忆方法:* (指针)和 const(常量) 谁在前先读谁 ;*象征着地址,const象征着内容;谁在前面谁就不允许改变

int a =;
int b = ;
int c = ;
int const *p1 = &b;//const 在前,定义为常量指针
int *const p2 = &c;//*在前,定义为指针常量

常量指针p1:指向的地址可以变,但内容不可以重新赋值,内容的改变只能通过修改地址指向后变换。

p1 = &a是正确的,但 *p1 = a是错误的。
指针常量p2:指向的地址不可以重新赋值,但内容可以改变,必须初始化,地址跟随一生。
    p2= &a是错误的,而*p2 = a 是正确的。

2.3 指针所占的位数

指针所占的位数不会因类型不同而不同,如int *p与double *q 在同一操作系统下所占的位数始终是相同的,只跟操作系统的位数(x64/x86)相关。

2.4 指针与数组之间的关系

在C语言中数组的名字就是数组的地址,数组的地址也等价于数组第一个元素的地址

例:

#include <stdio.h> // 这个头文件在系统目录下
#include <stdlib.h> // 使用了system函数 void main() { int a[] = {,,};
int *p = a;
int *q = &a[];
printf("变量p存放的地址为:%p\n变量q存放的地址为:%p\n",p,q); system("pause");
}

也可以通过指针来遍历当前数组元素:

#include <stdio.h> // 这个头文件在系统目录下
#include <stdlib.h> // 使用了system函数 void main() { int a[] = {,,};
int *p = a;
//int *q = &a[0];
//printf("变量p存放的地址为:%p\n变量q存放的地址为:%p\n",p,q);
for (int i = ; i < ; i++) {
printf("第%d个元素为:%d\n",i,p[i]);
}
system("pause");
}

三.指针的其它用法

3.1 指针的运算

#include <stdio.h> // 这个头文件在系统目录下
#include <stdlib.h> // 使用了system函数 // 指针的运算
void point_test_1(); // 清除数组中的值(p的值发生改变)
void clearArrayValueByPoint(); // 清除数组中的值(p的值不发生改变)
void clearArrayValueByPoint2(); // 使用指针来进行冒泡排序
void sortByPoint(); // 任何一种数据类型都是char的数组
void point_test_2();
void point_test_3(); // 表示IP地址 192.168.111.123
void ipAddressByPoint(); void main() { ipAddressByPoint();
system("pause");
} // 指针的运算
void point_test_1() { int *p1;
char *p2;
int a1 = ;
char a2 = ;
p1 = &a1;
p2 = &a2;
printf("%p,%p\n", p1, p2); p1++; // 位移了4个字节,4个sizeof(int)
p2++; // 位移了1个字节,1个sizeof(char)
printf("%p,%p\n", p1, p2); p1 += ; // 位移了4*sizeof(int) = 16
p2 += ; // 位移了4*sizeof(char) = 4
printf("%p,%p\n", p1, p2); p1 -= ; // 向前移动了2*sizeof(int)
p2 -= ; // 向前移动了2*sizeof(char)
printf("%p,%p\n", p1, p2); } // 清除数组中的值(p的值发生改变)
void clearArrayValueByPoint() { int array[] = {,,,,,,,,,}; int *p = array; for (int i = ; i < sizeof(array) / sizeof(int); i++) {
*p = ;
p++;
} for (int i = ; i < sizeof(array) / sizeof(int); i++) {
printf("array[%d]=%d\n", i, array[i]);
}
} // 清除数组中的值(p的值不发生改变)
void clearArrayValueByPoint2() { int array[] = { , , , , , , , , , }; int *p = array; for (int i = ; i < sizeof(array) / sizeof(int); i++) {
*(p + i) = ; // 循环完成之后,p仍然指向首元素地址
} for (int i = ; i < sizeof(array) / sizeof(int); i++) {
printf("array[%d]=%d\n", i, p[i]); // 因为此时p仍然指向首元素地址,所以可以使用p[i]来输出各个元素
}
} // 使用指针来进行冒泡排序
void sortByPoint() { int array[][] = { { , , , }, { , , , }, {,,,} };
int *p = array; // array可理解成里面有12个元素的array[12],任何一个多维数组都可以理解成一个一维数组 // 排序
for (int i = ; i < ; i++) { for (int j = ; j < -i;j++) {
if (p[j] < p[j-]) {
int tmp = p[j];
p[j] = p[j - ];
p[j - ] = tmp;
}
}
} // 输出
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++) {
printf("array[%d][%d]=%d\t",i,j,array[i][j]);
}
printf("\n");
} } // 任何一种数据类型都是char的数组
void point_test_2() { int a = 0x12345678;
unsigned char *p = (char *)&a;
printf("%x, %x, %x, %x\n",p[],p[],p[],p[]); } void point_test_3() { int a = 0x12345678;
unsigned char *p = (char *)&a;
printf("%x, %x, %x, %x\n", p[], p[], p[], p[]);
p[] = ;
printf("%x\n",a); } // 表示IP地址 192.168.111.123
void ipAddressByPoint() { unsigned int a = ;
unsigned char *p = (char *)&a;
p[] = ;
p[] = ;
p[] = ;
p[] = ;
printf("%u\n",a); }

3.2 多级指针

#include <stdio.h> // 这个头文件在系统目录下
#include <stdlib.h> // 使用了system函数 // 多级指针
void multiPoint(); // 二级指针
void multiPoint1(); // 三级指针
void multiPoint2(); void main() { multiPoint2();
system("pause");
} // 多级指针
void multiPoint() { int *a[] = { };
int **p = a;
int array[] = { , , , , , , , , , }; for (int i = ; i < ; i++) {
a[i] = &array[i]; // 将指针数组中的每一个指针指向数组中的每一个元素
} for (int i = ; i < ; i++) {
printf("array[%d]=%d\n", i, *p[i]); // 取出每个指针对应的数组内容
}
} // 二级指针
void multiPoint1() { int a = ;
int *p = &a;
int **q = &p; // 二级指针不能直接指向int的地址,而是指向一个int*的地址
// 修改a的值
**q = ;
printf("a=%d\n",a);
} // 三级指针
void multiPoint2() { int a = ;
int *p = &a;
int **q = &p; // 二级指针不能直接指向int的地址,而是指向一个int*的地址
int ***z = &q;
// 修改a的值
***z = ;
printf("a=%d\n", a);
}

3.3 指针变量当做函数的参数

#include <stdio.h> // 这个头文件在系统目录下
#include <stdlib.h> // 使用了system函数 // 将指针变量用作函数的参数
void pointAsFunctionArgus(int *a); // 交换两个数的值
void myswap(int *a1, int *a2); void main() { //int a = 10;
//// 将a的值+1
//pointAsFunctionArgus(&a);
//printf("a=%d\n",a); int a = ;
int b = ;
// 交换a,b的值
myswap(&a,&b);
printf("a=%d\nb=%d\n",a,b); system("pause");
} // 交换两个数的值
void myswap(int *a1, int *a2) { int tmp = *a1;
*a1 = *a2;
*a2 = tmp; } // 将指针变量用作函数的参数
void pointAsFunctionArgus(int *a) {
(*a)++;
}

3.4 数组名作为函数参数

当数组名作为函数参数时,C语言将数组名解释为指针,以下三种写法等价:

int func(int array[10]);

int func(int array[]);

int func(int *array)

如果数组名作为函数的参数,那么这个就不是数组了,而是一个指针变量。

在C语言中,如果把数组名作为函数的参数,那么在函数内部就不知道这个数组的元素个数了,需要再增加一个参数来标明这个数组的大小。

#include <stdio.h> // 这个头文件在系统目录下
#include <stdlib.h> // 使用了system函数 // 把数组名作为函数参数
void arrayNameAsFunctionArgus(int array[]); // 将数组内容输出
void myprint(const int *a, unsigned int n); // 冒泡排序
void bubble(int *a, unsigned int n); void main() { //int array[10] = {1,2,3,4,5,6,7,8,9,10};
//arrayNameAsFunctionArgus(array); int array[][] = { { , , , }, { , , , }, {,,,} };
bubble(array,sizeof(array) / sizeof(int));
myprint(array, sizeof(array) / sizeof(int)); system("pause");
} // 冒泡排序
void bubble(int *a,unsigned int n) { for (int i = ; i < n; i++) {
for (int j = ; j < n - i; j++) {
if (a[j] < a[j-]) {
int tmp = a[j];
a[j] = a[j - ];
a[j - ] = tmp;
}
}
}
} // 将数组内容输出
void myprint(const int *a, unsigned int n) { for (int i = ; i < n; i++) {
printf("%d\n",a[i]);
} } // 把数组名作为函数参数,当数组名作为函数参数时,C语言将数组名解释为指针(下列三种写法等价)
//void arrayNameAsFunctionArgus(int array[])
//void arrayNameAsFunctionArgus(int *array)
void arrayNameAsFunctionArgus(int array[]) {
*array = ; // 这种写法很危险!!!
array += ; // 1.使用者可以修改原始数组里的值,2.若使用者对指针进行运算,还会发生越界!!!正确写法: func(const int array[])
// const 保护形参的值不被改变 for (int i = ; i < ; i++) {
printf("%d\n",array[i]);
}
}

四.memset、memcpy,memmove函数

4.1 memset函数

#include <string.h>

void *memset( void *buffer, int ch, size_t count );

功能:设置一块内存区域。主要作用:把一块内存重新设置为0

参数说明:第一个参数是内存首地址,第二个参数是要设置的值,第三个参数是这块内存的大小,单位:字节。

4.2 memcpy函数

#include <string.h>

void *memcpy( void *to, const void *from, size_t count );

功能:内存拷贝。使用memcpy的时候要注意内存区域不能重叠。

参数说明:第一个参数是目标内存首地址,第二个参数是源内存首地址,第三个参数是拷贝字节数

4.3 memmove函数

#include <string.h>

void *memmove( void *to, const void *from, size_t count );

功能:内存移动,参数与memcpy一致。

参数说明:第一个参数是目标内存首地址,第二个参数是源内存首地址,第三个参数是拷贝字节数

示例代码:

#include <stdio.h> // 这个头文件在系统目录下
#include <stdlib.h> // 使用了system函数
#include <string.h> // 使用memset、memcpy、memmove函数 // memset函数的使用
void memsetDemo(); // memcpy函数的使用(使用memcpy的时候需要注意内存区域不能重叠)
void memcpyDemo(); // memmove函数的使用
void memmoveDemo(); void main() { memmoveDemo();
system("pause");
} // memmove函数的使用
void memmoveDemo() { int a1[] = { , , , , , , , , , };
int a2[];
memmove(a2, a1, sizeof(a1)); // 输出a2的值
for (int i = ; i < sizeof(a2) / sizeof(int); i++) {
printf("a2[%d]=%x\n", i, a2[i]);
} } // memcpy函数的使用
void memcpyDemo() {
int a1[] = {,,,,,,,,,};
int a2[];
memcpy(a2,a1,sizeof(a1)); // 输出a2的值
for (int i = ; i < sizeof(a2) / sizeof(int); i++) {
printf("a2[%d]=%x\n",i,a2[i]);
} } // memset函数的使用
void memsetDemo() { char array[] = { };
array[] = ;
array[] = ;
array[] = ;
// 把这块内存的所有成员再次初始化为0
memset(array,,sizeof(array)); for (int i = ; i < sizeof(array) / sizeof(char); i++) {
printf("array[%d]=%d\n",i,array[i]);
}
}

五.字符数组与字符串指针

C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中:

#include <stdio.h>
int main(){
char str[] = "http://www.baidu.com";
int len = strlen(str), i;
//直接输出字符串
printf("%s\n", str);
//每次输出一个字符
for(i=; i<len; i++){
printf("%c", str[i]);
}
printf("\n");
return ;
}

运行结果:
http://www.baidu.com
http://www.baidu.com

或者是定义字符串指针,使用这种方式输出:

#include <stdio.h>
#include <string.h> int main()
{
char *str = "http://www.baidu.com";
int len = strlen(str), i;
   
for (i = ; i < len; i++) {
printf("%c", *str++);
}
printf("\n"); return ;
}

输出结果:
http://www.baidu.com

那这两种方式输出的区别在哪呢?区别在于:字符数组存储在全局数据区或者是栈区,而指针指向的字符串存储在常量区。全局数据区或栈区的字符串有读取和写入的权限,而常量区的字符串只有读取的权限,没有写入权限。内存权限不同导致的一个显著的结果就是:字符数组可以在定义后读取和修改每个字符,而对于第二种形式的字符串,一旦被定义后,则不能对其进行修改。

例:修改字符串中第一个字符为a

#include <stdio.h>
#include <string.h> int main()
{ char *str = "http://www.baidu.com"; int len = strlen(str), i;
for (i = ; i < len; i++) {
printf("%c", *str++);
}
printf("\n"); *str = 'a'; return ;
}

运行结果:

http://www.baidu.com
Bus error: 10

出现了段错误。

C语言基础(16)-指针的更多相关文章

  1. GO学习-(10) Go语言基础之指针

    Go语言基础之指针 区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,是安全指针. 要搞明白Go语言中的指针需要先知道3个概念:指针地址.指针类型和指针取值. Go语言中的指针 任何程序数 ...

  2. Go语言基础之指针

    区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,是安全指针. 要搞明白Go语言中的指针需要先知道3个概念:指针地址.指针类型和指针取值. Go语言中的指针 Go语言中的函数传参都是值拷贝 ...

  3. 3.1 Go语言基础之指针

    区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,是安全指针. 要搞明白Go语言中的指针需要先知道3个概念:指针地址.指针类型和指针取值. 一.Go语言中的指针 Go语言中的函数传参都是值 ...

  4. 黑马程序员_ C语言基础之指针(三)

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 概览 指针是C语言的精髓,但是很多初学者往往对于指针的概念并不深刻,以至于学完之后随着时间的推移 ...

  5. 嵌入式-C语言基础:指针是存放变量的地址,那为什么要区分类型?

    指针是存放变量的地址,那为什么要区分类型?不能所有类型的变量都用一个类型吗?下面用一个例子来说明这个问题. #include<stdio.h> int main() { int a=0x1 ...

  6. 嵌入式-C语言基础:指针

    指针就是地址,变量的值可以通过两种方式访问,一个是通过变量名,一个是通过地址访问. 从而引出一个问题,即什么是指针变量?整型(字符)变量就是存放整形(字符)的变量,指针变量就是存放指针的变量,也就是存 ...

  7. C语言基础:指针类型与指针和数组、字符串的关系

    //指针变量就是用来存储地址的,只能存储地址 格式:  int  *p;  这个p为指针变量:指针变量占8个字节 类型是用来说明这个指针指向的类型: 比如上边的int代表这个指针变量会指向int类型的 ...

  8. C语言基础:函数指针 分类: iOS学习 c语言基础 2015-06-10 21:55 15人阅读 评论(0) 收藏

    函数指针:指向函数的指针变量. 函数名相当于首地址. 函数指针定义:返回值类型  (*函数指针变量名)(参数类型1,参数类型2,....)=初始值 函数指针类型:返回值类型  (*)(参数类型1,参数 ...

  9. C语言基础:初级指针 分类: iOS学习 c语言基础 2015-06-10 21:50 30人阅读 评论(0) 收藏

    指针:就是地址. &   取地址运算符 %p   打印地址占位符 int a=0; printf("%p ",&a);    指针变量:用来存放地址的变量 定义: ...

随机推荐

  1. [BZOJ 1789] Necklace

    Link: BZOJ 1789 传送门 Solution: 感觉$n\le 50$可以随便乱搞啊…… 这里我是先找到3条链的失配位置,再找到这之后其中2条链最远的失配位置,统计即可 Code: #in ...

  2. [Codeforces 30D] Kings Problem

    Brief Intro: 有n+1个点,其中n个点在X轴上,求从第k个点出发最短的汉密尔顿路径 Solution: 分类讨论+逐个枚举 设dist(i)是第i个点到n+1的距离 cal1(l,r)是n ...

  3. [xsy2962]作业

    题意:$f_0=1-\dfrac1e,f_n=1-nf_{n-1}$,求$f_n(n\leq10000)$,保留四位小数 这题代码只有⑨行但是题解很神... 因为递推式中有乘法,所以直接按题目来推肯定 ...

  4. OC语言基础之NSArray

    0.数组的分类 NSArray :不可变数组 NSMutableArray : 可变数组 1: // @[] 只创建不可变数组NSArray 2: /* 错误写法 3: NSMutableArray ...

  5. CASS 7.1 和 AutoCAD 2006的安装使用

    CAD 2006由于是一个古老的版本,所以在WIN7,WIN10上直接安装的话,一般无法成功.此外,AutoCAD 2006是不分32位64位的,之后的版本都是区分的. 但是,对于我这种几年不用一次C ...

  6. html里嵌入CSS的三种方式

    在HTML中定义CSS的方式有:Embedding(嵌入式).Linking(引用式).Inline(内联式),下面通过实例为大家详细介绍下它们的特点   在HTML中常用以下3种方式定义CSS:Em ...

  7. css活用,评分点击星星

    1.字符图集 2.css样式 .cleanfloat::after{display: block; clear: both; content:""; visibility: hid ...

  8. 关于java的关键字 transient

    我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,Java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable ...

  9. dot language 学习笔记

    dot language 学习笔记 UP | HOME   dot language 学习笔记 Table of Contents 1 dot 语言简介 2 基本语法 2.1 常用图形 2.2 常用线 ...

  10. dx12 memory management

    https://msdn.microsoft.com/en-us/library/windows/desktop/dn508285(v=vs.85).aspx Map with D3D11_MAP_W ...