0.展示PTA总分
1.本章学习总结
1.1 指针定义、指针相关运算、指针做函数参数。
指针定义
指针指向的是一片内存地址:
一般用定义数据类型的符号 和‘*’ 来定义指针
int *p = &a;//定义指针p,取地址a 让p指向a。
指针相关运算
- 指针变量加/减一个整数
例如:p++,p--,p+i等。
C语言与 C++语言规定,一个 指针变量加/减一个 整数是将该指针变量的原值(是一个地址)和它指向的变量所占用的内存单元 字节数相加或相减。这样就保证了p+i指向p下面的第i个元素。
如p+i代表这样的地址计算:p+i*d,其中d为p所指向的 变量单元所占用的 字节数。
指针变量赋值
C语言与C++语言规定,可以将一个 变量或一个函数的入口地址赋值给相应的指针变量。
例如(假设p是相应的指针):
p=&a;
p=max;(max为已经定义的函数)
- 指针变量相减
如果两个 指针变量指向同一个 数组的元素,则两个指针变量之差是两个指针之间的元素个数。
注意, 指针变量相加无实际意义。
- 指针变量比较
如果两个指针变量指向同一个数组的元素,则可以进行比较。指向前面元素的 指针变量小于指向后面元素的指针变量。
指针做函数参数
因为指针指向的是一片内存空间,所以用指针作为函数的参数,在函数体中,指针指向的形参的数改变了,那么指针指向的
那片内存空间的值也会随之改变,所以指针可用于需要函数体传回多个值的时候使用。
#include<stdio.h>
void fun(int* x)
{
*x = 3;//此时a那片内存的值为3,即使函数fun结束,a的值也不会消失
}
int main()
{
int a;
int* p = &a;
fun(&a);//也可以直接传入指向a的指针,即传入一个地址:
// fun(p);
return 0;
}
1.2 字符指针
1.2.1指针如何指向字符串
字符串可以由一个指针指向
const char *p = "hellow";
第一种直接用指针指向的无法改变其中的字符。
或者一个数组存放,指针指向该数组
char a[] = "hellow";
char *p = a;
用数组赋值的能更改其中的字符
1.2.2字符串相关函数及函数代码原型的理解
- strcpy
char *strcpy(char *strDest, const char *strSrc)
{
baichar *r = strDest;
while(*strDest++=*strSrc++);//把strSrc复制给strDest
*strDest = 0;
return r;
}
- strlen
int strlen(const char* str)
{
if (NULL == str)
throw"Invalid Argument!";
int len;
for (len = 0; *str&&*str != '
'; str++)//算出字符串的长度
len++;
return len;
}
- strcat
char *strcat(char *strDest, const char *strScr)
{
char * address = strDest;
if(!strDest||!strScr) return NULL;//防止传入空指针
while(*strDest)
{
strDest++; //遍历字符串直到NULL
}
while(*strDest++ = *strScr++) //将strScr字符串连接到strDest之后
{
NULL;
}
return address;
}
- strcmp
int strcmp( const char* src,const char* dst)
{
int ret = 0;
while (!(ret = *(unsigned char*)src - *(unsigned char*)dst) && *dst)//逐个比较相对应的字符串的字符的大小
++src, ++dst;
if (ret < 0)
ret = -1;
else if (ret > 0)
ret = 1;
return(ret);
}
1.2.3字符串相关函数用法
1.3 指针做函数返回值
int *p()
{
reutrn p;//返回地址p
}
注意:
1.如果p在函数中作了位运算,则返回的是该指针做了位运算的地址所以,
一般需要在开始时定义一个指针指向p的首地址,然后返回该新定义的指针。
2.返回的指针所指的内容的生存周期因为全局变量,如果在函数中定义了一片内容,
然后返回指向该内容的指针则会报错。
3.返回的指针需要作为判断时,也可返回NULL
1.4 动态内存分配
1.4.1为什么要动态内存分配
- 要用一片连续的内存,如数组时,则可以用多少申请多少,不需要一开始把数组定义很大,占用多余的空间。
- 在函数中申请可延长该数据的生存周期,直到程序结束或者free。
1.4.2堆区和栈区区别
1、栈区(stack)
由编译器自动分配释放 ,存放函数的参数值,局部变量的值等,内存的分配是连续的,类似于平时我们所说的栈,如果还不清楚,那么就把它想成数组,它的内存分配是连续分配的,
即,所分配的内存是在一块连续的内存区域内.当我们声明变量时,那么编译器会自动接着当前栈区的结尾来分配内存.
2、堆区(heap)
一般由程序员分配释放, 若程序员不释放,程序结束时可能由操作系统回收.类似于链表,在内存中的分布不是连续的,它们是不同区域的内存块通过指针链接起来的.
一旦某一节点从链中断开,我们要人为的把所断开的节点从内存中释放.
1.4.3动态内存分配相关函数及用法
头文件 #include<stdlib.h>
+malloc
void * malloc (size_t size);
malloc的使用比较直接,一个成功的malloc调用返回分配的size大小的内存的指针。失败时返回NULL并将错误代码置为ENOMEM。
教材中经常出现的用法是将malloc返回的void指针进行强制内存转换然后赋给内存指针,其实是不必要的,在赋值时C语言是可以将void类型指针自动转换成对应的指针的。
- calloc
void * calloc (size_t nr, size_t size);
calloc可以分配nr个size大小的内存空间,一般用于一组struct结构体的分配。
那么calloc和malloc有什么区别呢?抛开nr参数不谈(malloc也可以将参数设置为nr*size达到一样的效果),
最关键的区别是malloc分配的内存是不保证初始化的,而calloc会将分配的内存都初始化为0.
+relloc
void * realloc (void *ptr, size_t size);
realloc函数将ptr指向的内存空间重新分配大小为size并返回新的内存首地址。具体的实现,函数首先会尝试直接在已经分配的内存后进行padding,如果空间足够那么还是返回原来的地址,
如果不够,则会寻找新的空间并malloc size个字节,之后再将原先的内容“搬家”到新的内存地址,所以函数的返回值可能和原指针相同,也可能不同。
另外,size参数如果是0,则该函数和free效果相同。如果ptr是NULL,函数的效果和malloc相同~
注意用完这片内存后用free释放
void free (void *ptr);
释放前三个函数申请的内存空间。关于free最经典的问题就是内存泄露(memory leak)。所以,使用前三个分配函数分配的内存一定要记得free掉。
1.4.4 举例为多个字符串做动态内存要如何分配
char (*p)[10]
p = (char(*)[10])calloc(n,sizeof(char[10]))
char *a[10]
a = (char*)calloc(n,sizeof(char))
1.5 指针数组及其应用
- 指针数组:用于存放字符指针的数组,仅用来存放指针,所以它指向的每个字符串的首地址均可以改变,
由于指针指向一个地址,所以字符串的最大长度可以改变
char *p[n]//n为行的数量
- 二维数组:二维数组本质上是两个一维数组的合成,定义时就已经分配给二维数组空间。
1.6 二级指针
即指向一个一级指针的地址指针
int **p;
int *a;
int n;
p = &a;//二级指针p指向一级指针a
a = &n;//一级指针a指向数n,则p最终也指向n的内容
1.7 行指针、列指针
行指针
等同于int (*p)[3]
则a为一个行指针,即a指向一个行的首地址,不指向其中的具体内容
可以用a来寻址各行的内容
或者移动到下一行p++
列指针
列指针与一个一级指针类似
即所指的是具体的内容
int a[3][3]
int *p =a[0]
2.PTA实验作业
2.1 7-5 删除字符串中的子串
2.1.1 伪代码
定义s和删除的del字符串
while(strstr(s,del)!=NULL)//重复寻找直至没有需要删除的字符串
计算此时两个字符串的长度
strcpy(temp,strstr(s,del))//将重复字符包括后面的字符串复制给temp
计算temp的长度
strcpy(s+len_s-loc,temp+len_del)/*s+s的长度到最后一个字符减去loc
则到要删除的字符串的首地址,将temp+删除字符的长度,则为删去字符串后
的字符串,复制到原字符串后面。*/
end while
2.1.2 代码截图
2.1.3 找一份同学代码(尽量找思路和自己差距较大同学代码)比较,说明各自代码特点
骆锟宏同学的代码是找到了子串然后进到函数进行一次删除,使指针的指向更清晰。
2.2 合并2个有序数组
2.2.1 伪代码
定义用来排序的数组c//用选择法或者冒泡法会超时
for 0 to m
c[a[i]]++
if(a[i]>max)a[i] = max 寻找最大值,在遍历c时有个上限
end if
end for
for 0 to n
和第一个for循环相同 遍历数组b,寻找最大值
for 0 to max
if(c[i]>0)
for 0 to c[i]
a[j] = i//将排序好的数存入a数组
end if
end for
end for
2.2.2 代码截图
2.2.3 找一份同学代码(尽量找思路和自己差距较大同学代码)比较,说明各自代码特点。
林进源同学的代码是边比较边排序,少了许多循环,占用更少的内存,从最后面往前排序,也不会使原来的还未排序数被覆盖。
2.3 说反话-加强版
2.3.1 伪代码
定义str的长度len,i = len -1
while
while(str[i]不为空格及i>=0时)
i-- count ++
end while
if(count >=1)
for 0 to count
使a存放str的元素//将str的元素倒放过来
end for
end if
if(word ==1&&a[j-1] ==' ')
a[j-1] = 0 //特殊判断,当仅有一个字母时 且前面都为空格
end if
2.3.2 代码截图
2.3.3 请说明和超星视频做法区别,各自优缺点。
大概思路相同,都是用if来判断处理各类特殊条件。