指针
这个作业属于哪个班级 | C语言--网络2011/2012 |
---|---|
这个作业的地址 | C博客作业05--指针 |
这个作业的目标 | 学习指针相关内容 |
姓名 |
(0----2)
展示关于“指针题目集”分数截图。
1.本章学习总结(3分)
整理指针
主要知识点,必须包含内容有:
1.1 指针定义、指针相关运算、指针做函数参数。
1.1.1指针定义
1.1.1.1什么是指针
> C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里,数据对象是指存储在内存中的一个指定数据类型的数值或字符串,它们都有一个自己的地址,而指针便是保存这个地址的变量。也就是说:指针是一种保存变量地址的变量。C语言指针详解
1.1.1.2如何声明一个指针
int *p; // 声明一个 int 类型的指针 p
char *p // 声明一个 char 类型的指针 p
int *arr[10] // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向 int 类型对象的指针
int (*arr)[10] // 声明一个数组指针,该指针指向一个 int 类型的一维数组
int **p; // 声明一个指针 p ,该指针指向一个 int 类型的指针
1.1.1.3如何初始化一个指针
* 方法1:使指针指向现有的内存 */
int x = 1;
int *p = &x; // 指针 p 被初始化,指向变量 x ,其中取地址符 & 用于产生操作数内存地址
/* 方法2:动态分配内存给指针 */
int *p;
p = (int *)malloc(sizeof(int) * 10); // malloc 函数用于动态分配内存
free(p); // free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用,要使用这两个函数需要头文件 stdlib.h
指针必须进行初始化,不然会成为野指针,指针就会乱指
观野指针有感
1.1.1.4指针的运算
#include "stdio.h"
int main(){
int a[10] = {1,2,3,4,5,6,7,8,9,0};
int sub;
int *p1 = &a[2];
int *p2 = &a[8];
int *p=a;//默认从a[0]开始,因为数组是特殊的指针
sub = p2-p1;
printf("%d\n",sub); // 输出结果为 6
printf("%d\n",*p++); // 输出结果为 1因为*的优先级比++高
printf("%d\n",*(p++)); // 输出结果为 2
return 0;
}
1.1.1.5指针数组
int *p[10]; // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向int类型的指针
1.1.1.6数组指针
int (*p)[10]; // 声明一个数组指针 p ,该指针指向一个数组
#include "stdio.h"
int main(){
int arr[2][3] = {1,2,3,4,5,6}; // 定义一个二维数组并初始化
int (*p)[3]; // 定义一个数组指针,指针指向一个含有3个元素的一维数组
p = arr; // 将二维数组的首地址赋给 p,此时 p 指向 arr[0] 或 &arr[0][0]
printf("%d\n",(*p)[0]); // 输出结果为 1
p++; // 对 p 进行算术运算,此时 p 将指向二维数组的下一行的首地址,即 &arr[1][0]
printf("%d\n",(*p)[1]); // 输出结果为5
return 0;
}
1.2 字符指针
包括指针如何指向字符串、字符串相关函数及函数代码原型的理解、字符串相关函数用法(扩展课堂未介绍内容)
1.2.1字符串是什么
C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中
#include <stdio.h>
#include <string.h>
int main(){
char str[] = "abcdefg";
int len = strlen(str), i;
printf("%s\n", str); //直接输出字符串,结果为abcdefg
for(i=0; i<len; i++)
{
printf("%c", str[i]); //每次输出一个字符结果为abcdefg
}
printf("\n");
return 0;
}
1.2.2指针指向字符串
char *str = "abcd"; //法一
char *str;
str = "abcd"; //法二
1.2.3字符串相关函数
#include <stdio.h>
#include <string.h>
int main(void)
{
char string[10];
char *str1 = "abcd";
char *str2 = "efg";
char str3[]="abc"
char a='b'
stpcpy(string, str1); //拷贝一个字符串到另一个
strcat(str1,str2); //字符串拼接
strchar(str1,a); //在一个串中查找给定字符的第一个匹配之处
strcmp(char *str1, char *str2); //串比较
strrev(str3); //串倒转,若改为*str3 = "abcxyz"则会崩溃
strstr(str1, str3) //用于判断字符串str3是否是str1的子串。如果是,则该函数返回str3在str1中首次出现的地址;否则,返回NULL。
return 0;
}
1.3 指针做函数返回值
#include "stdio.h"
void swap1(int a,int b) // 参数为普通的 int 变量
{
int temp;
temp = a;
a = b;
b = temp;
}
void swap2(int *a,int *b) // 参数为指针,接受调用函数传递过来的变量地址作为参数,对所指地址处的内容进行操作
{
int temp; // 最终结果是,地址本身并没有改变,但是这一地址所对应的内存段中的内容发生了变化,即x,y的值发生了变化
temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 1,y = 2;
swap1(x,y); // 将 x,y 的值本身作为参数传递给了被调函数
printf("%d %5d\n",x,y); // 输出结果为:1 2
swap2(&x,&y); // 将 x,y 的地址作为参数传递给了被调函数,传递过去的也是一个值,与传值调用不冲突
printf("%d %5d\n",x,y); // 输出结果为:2 1
return 0;
}
1.4 动态内存分配
1.4.1为什么要动态内存分配
- 数组的长度必须事先指定,而且只能是常量,不能是变量。比如像下面这么写就是对的:
int a[5];
而像下面这么写就是错的:
int length = 5;
int a[length]; //错误
-
因为数组长度只能是常量,所以它的长度不能在函数运行的过程当中动态地扩充和缩小。
-
对于数组所占内存空间程序员无法手动编程释放,只能在函数运行结束后由系统自动释放,所以在一个函数中定义的数组只能在该函数运行期间被其他函数使用。
而动态内存就不存在这个问题,因为动态内存是由程序员手动编程释的,所以想什么时候释放就什么时候释放。只要程序员不手动编程释放,就算函数运行结束,动态分配的内存空间也不会被释放,其他函数仍可继续使用它。除非是整个程序运行结束,这时系统为该程序分配的所有内存空间都会被释放。
1.4.2堆区和栈区区别
1)申请方式: 栈区内存由系统自动分配,函数结束时释放;堆区内存由程序员自己申请,并指明大小,用户忘释放时,会造成内存泄露,不过进程结束时会由系统回收。
2)申请后系统的响应: 只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则将报异常提示栈溢出;堆区,空闲链表,分配与回收机制,会产生碎片问题(外部碎片)-->(固定分区存在内部碎片(分配大于实际),可变分区存在外部碎片(太碎无法分配))。
3)申请大小的限制:栈是1或者2M,可以自己改,但是最大不超过8M;堆,看主机是多少位的,如果是32位,就是4G
4)申请效率:栈由系统自动分配,速度较快,程序员无法控制;堆是由new分配的内存,一般速度较慢,而且容易导致内存碎片,但是用起来方便!
5)存储内容:栈,函数调用(返回值,各个参数,局部变量(静态变量不入栈));堆,一般在堆的头部用一个字节存放堆的大小,堆中的具体内容由程序员安排。
6)存取效率的比较:栈比堆快,Eg :char c[] = /"1234567890/";char *p =/"1234567890/";读取c[1]和p[1],c[1]读取时直接吧字符串中的元素读到寄存器cl中,而p[1]先把指针值读到edx中,再根据edx读取字符,多一次操作。
7)管理方式不同:栈,数据结构中的栈;堆,链表
8)生长方向:栈,高到低;堆,低到高堆区和栈区的区别
1.4.3动态内存分配相关函数及用法
1.4.3.1相关函数
int *p = (int *)malloc(n*sizeof(int));
int *p = (int *)calloc(n,sizeof(int));
free(p);
1.4.3.2举例为多个字符串做动态内存要如何分配
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int i, n = 0;
char *a[20], str[15];
scanf("%s", str);
while(str[0] != '#'){
a[n] = (char *) malloc( sizeof(char) * ( strlen(str) + 1 ));
strcpy(color[n], str);
n++;
scanf("%s", str);
}
for(i = n - 1; i >= 0; i--)
free(a[i]);
}
1.5 指针数组及其应用
多个字符串用二维数组表示和用指针数组表示区别?
int a[10][10];
for(int i=0;i<10;i++)
scanf("%S",a[0]); //二维数组表示
char *p[10];
for(int i=0;i<10;i++)
{
p[i]=(char*)malloc(1024);
scanf("%s",p[i]); //用指针数组表示
}
1.6 二级指针
1.6.1二级指针的简单理解
假若定义一个二级指针
int **q;
int**q 可以把它分为两部分看,即 int* 和 (q),后面 (q) 中的“”表示 q 是一个指针变量,前面的 int 表示指针变量 q 只能存放 int* 型变量的地址。
1.6.2二级指针延伸至多级指针
对于二级指针甚至多级指针,我们都可以把它拆成两部分。首先不管是多少级的指针变量,它都是一个指针变量,指针变量就是一个“”,其余的“”表示的是这个指针变量只能存放什么类型变量的地址。比如“int*p;”表示指针变量 p 只能存放 int 型变量的地址。
1.7 行指针、列指针
1.7.1行指针、列指针的定义
行指针:指的是一整行,不指向具体元素。
列指针:指的是一行中某个具体元素。
可以将列指针理解为行指针的具体元素,行指针理解为列指针的地址。
那么两个概念之间的具体转换是:
*行指针----列指针
&列指针----行指针
1.7.2用法
#include <stdio.h>
void main()
{
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int *p= a[0]; // 列指针的定义法
for(; p < a[0] + 12; p++)
{
printf("%d ",*p);
}
int (*p)[4]= &a[0]; // 行指针定义法或者int (*p)[4]= a;
int i, j;
for(i = 0; i < 3; i++)
for(j = 0; j < 4; j++)
{
printf("%d ",*(*(p + i) + j));
}
return 0;
}
2.PTA实验作业(7分)
2.1 字符串的冒泡排序(2分)
2.1.1 伪代码
int n, m;//定义输入的个数以及循环的次数
for(int i = 0; i < n; i++)
scanf("%s",s[i]);//循环输入字符串
for(int i = 0; i < m; i++)双循环进行比较
{
for(int j = 0; j < n - i - 1; j++)
{
if(strcmp(s[j],s[j+1]) > 0)
最后用循环输出即可
2.1.2 代码截图
2.1.3 同学代码。
2.2 合并2个有序数组(2分)
2.2.1 伪代码
int* c;
c = (int*)malloc((m + n) * sizeof(int));//定义一个新的指针*c,动态分配*c内存
int x = 0, y = 0, z = 0;//定义三个为零的数
while (x < m && y < n) //在x,y的共同值范围内进行判断和赋值
while (x < m) //在超过其中一方的范围内进行赋值
{
c[z++] = a[x++];
}
while (y < n)
{
c[z++] = b[y++];
}
最后将c的赋值给a即可
2.2.2 代码截图
2.2.3 同学代码
最大的不同就是在进行比较和赋值时倒着来并且没有重新定义一个指针用于比较和赋值,看起来简洁多了
2.3 说反话-加强版(3分)
2.3.1 伪代码
char s;
char t[500000];
int i = 0, count = 0, flag = 0;
while ((s=getchar()) != '\n')
{
if (s != ' ')
{
flag = 1;
t[i++] = s;
count = 0;
}
else if (count > 0)
{
continue;
}
else if (flag)
{
t[i++] = s; //只有之前遇到单词的情况下碰到空格才把这个空格写入目标字符串
count ++;
}//删除多余的空格,将目标字符串放入 t 中
count = 0;
int j;
for (i-=1; i>=0; i--)
{
if (t[i] != ' ')
{
count ++;//这里的 count 统计的是单词的位数
}
else if (t[i]==' ' && count > 0)
{
for (j=i+1; j<=i+count; j++)
{
printf("%c", t[j]);
} //遇到空格就输出单词
printf(" ");//遇到空格就输出单词
count = 0;// 还剩最后一个单词没输出
}
}
2.3.2 代码截图
2.3.3 请说明和超星视频做法区别,各自优缺点。
我的做法是将字符串的空格删掉然后计字符串的数再输出
超星视频的将这两个结合在一起明显比我的高明