一级指针、内存图、段错误

指针

优点

1、使程序更简洁、紧凑、高效
2、有效的表达更复杂的数据结构
3、动态分配内存
4、得到多于一个数的函数返回值

概念

地址:

内存中每个字节单位都有一个编号

指针:

指针就是地址

指针变量:

用于存放地址的变量就叫做指针变量

定义格式

<存储类型> <数据类型> *<指针变量名>
存储类型指的是auto,register, static,extern。其中 auto 可以省略。
指针变量的变量名是“ * ”后面的内容,“ * ”只是说明定义的是一个指针变量。

int a = 5;
int *p = &a;
char b = 'v';
char *q = &b;

printf("%p\n", p);			// 整型变量 a 的地址
printf("%p\n", q);			// 字符型变量 b 的地址

指针操作符

&: 取地址符,取变量的地址
*: 取内容,取地址里面存放的内容
*&a:取变量 a 的值 *&a <=> *p <=> a
( &*a:错误用法 )

区分:

p:指针变量,内容是地址。
*p:指针所指向的对象,内容是数据。
&p:指针变量所占用的 存储区域的 地址。

初始化

指针变量在使用在不仅要定义还要初始化。指针变量的值只能是变量的地址,不能是其他数据,否则将引起错误。未初始化的指针变量不能随便使用,会产生“野指针”(随机指向某个内存单元)。

将普通变量的地址赋值给指针变量

int a =10;
1)int  *p = &a;		// 定义的同时赋值
2)int  *p = NULL; 		// 先定义后赋值
	p = &a;		
int a = 10;
int *p = NULL;
p = &a;
printf("%d %d\n", a, *p); 		// 10 10
printf("%p %p\n", &a, p); 		// 打印的都是变量a地址
*p = 3;
printf("%d %d\n", a, *p); 		// 3 3
printf("%p %p\n", &a, p); 		// 打印的地址没有发生改变

将数组的首地址赋值给指针变量

char s[10] = "hello";
char *p = s;					// 指针指向数组的首地址
printf("%c\n", *p);				// h
printf("%s\n", *p);				// !!!!段错误!!!!

将指针变量里面保存的地址赋值给另一个指针变量

float a = 1.3;
float *p = &a;
float *q = p;
printf("%p %p\n", p, q);		// 打印的都是变量a地址

指针运算

算术运算

在这里插入图片描述

// p++; p--;
char s[32] = "hello";
char *p = s;
printf("%c\n", ++p); 		// p 现在指向了 e 


int *p; 		p++; 				// 移动 4 字节,指针指向发生变化
double *p; 		p++; 				// 移动 8 字节,指针指向发生变化

									// p + n 或 p - n,指针指向不发生变化。
// p+n; p-n;
// p + n 表示的实际内存单元的地址量是:p + sizeof(p的类型) * n
// p - n 表示的实际内存单元的地址量是:p - sizeof(p的类型) * n

int arr[32] = {1,2,3,5};
int *p = arr;
int num;
p = p+3;						// 指向5
int *q = arr;					// 指向1
printf("%d\n", p-q);			// 3


// 两个地址之间的差 = 两个地址之间相差元素的个数
// 即,p-q 事实上等于 (p-q) / sizeof(p或q的类型)
// p-q;
int m = 100;
int *p1 = NULL, *p2 = NULL;
p1 = &m;
p2 = p1 + 2;
printf("p1=%p p2=%p\n", p1, p2);		// 0x地址,0x地址+2*4
printf("p2-p1=%d\n", p2-p1); 			// 2


double n = 200;
double *p1 = NULL, *p2 = NULL;
p1 = &n;
p2 = p1 + 2;
printf("p1=%p p2=%p\n", p1, p2);		// 0x地址,0x地址+2*8
printf("p2-p1=%d\n", p2-p1); 			// 2

运行结果如下:
在这里插入图片描述

关系运算

>    <		>=		<=	 	==    !=

指针之间关系运算比较的是它们指向地址的高低

指向高地址的指针大小 大于 指向低地址的指针
char s[10] = "hello";
char *p1 = &s[1];
char *p2 = &s[3];
if(p1 < p2)
    printf("p2大");			// 执行
else
    printf("p1大");			// 不执行

注意:
指向不同类型的数组指针关系运算没有意义,
指向不同区域的指针关系运算也没有意义 (应同一个数组间进行比较)
指针与一般整数变量之间的关系运算没有意义,但可以和 0 比较是否相等,判断指针是否为空

赋值运算

见上面,初始化。

指针的大小

int a = 5;
int *p1 = &a; 
short b = 2;
short *p2 = &b; 
double c = 3.333;
double *p3 = &c; 
double *p4 = NULL; 
printf("%d\n", sizeof(p1));			// 4
printf("%d\n", sizeof(p2)); 		// 4
printf("%d\n", sizeof(p3)); 		// 4
printf("%d\n", sizeof(p4)); 		// 4


32位操作系统:指针为四个字节
// 32位:表示单次运算可以处理最大数据的位数
64位操作系统:指针为八个字节
// 类似地,...

内存图

内存地址是固定的,但是变量的地址不固定(栈区变量随机分配)。
在这里插入图片描述

段错误 Segmentation fault (core dumped)

1)“野指针”,没有规定指向的指针,会随机指向一个内存单元。
“野指针”产生原因,主要有两种:
① 指针变量没有初始化;
② 指针 p 被 free 之后,没有置NULL,程序会误以为 p 是合法指针
解决方法:int *p = NULL;
2)内存泄漏,对非法空间进行赋值或访问。

练习

1)字符串 ——> 整型数字

要求:字符串为 0-9 组成,输出数据为一个整型数
char s[10] = “123456”;

printf(“%d\n”, num); // num = 123456

#include <stdio.h>

#define N 10

int main(int argc, char const *argv[])
{
    char s[N];
    int num = 0;
    char *p;

    scanf("%s", s);             // 不能循环输入单个字符,否则效果不能实现

    p = s;
    while (*p){
        num = num * 10 + (*p - 48);
        p++; 
        // printf("%d %d\n", *p, num);
    }

    printf("%d\n", num);

    return 0;
}

运行结果如下:
在这里插入图片描述

2)字符串倒置

字符串倒置(用指针实现) 如: hello ->olleh
思路:定义两个指针分别指向字符串的开头和结尾,前面的指针向后走,后面的指针向前走

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char str[20];
    gets(str);
    char *p = str, *q = str + strlen(str)-1;
    char temp;

    for (int i = 0; i < strlen(str)/2; i++){			// while (p < q)
        temp = *p;
        *p = *q;
        *q = temp;
        p++;
        q--;
    }
    puts(str);

    return 0;
}

运行结果如下:
在这里插入图片描述

3)间隔输出字符串

将字符串 "Computer Science"赋值给一个字符数组,
然后从第一个字母开始间隔的输出该字符串,用指针完成,
结果:Cmue cec

#include <stdio.h>

int main(int argc, char const *argv[])
{
    char str[] = "Computer Science"; 	
    char *p = str; 

    for (int i = 0; *p != '\0'; i++){ 
        printf("%c", *p);      // 省略数组长度时,最好使用 *p != '\0' 而不是 str[i] != '\0'
        p += 2;					// 也可以直接 while(*p)
    }
    putchar('\n');

    return 0;
}

4)合并字符串
将两个字符串合并为一个字符串并且输出,用指针实现
char str1[32] = "hello ", str2[32] = “world”
结果:hello world

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char str1[32] = "hello ", str2[32] = "world";
    char *p = str1 + strlen(str1), *q = str2;
    for (; *q != '\0';){
        *p = *q;
        p++;
        q++;
    }
    puts(str1);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值