这个作业属于哪个班级 | C语言–网络2011/2012 |
---|---|
这个作业的地址 | C语言博客作业05–指针 |
这个作业的目标 | 学习指针相关内容 |
姓名 | 骆锟宏 |
int *ptr; 整型指针,指向整型变量
double *fPtr;浮点型指针,指向浮点型变量
char *cPtr; 字符型指针,指向字符型变量
1.’&‘表示取地址的运算符,运算对象是内容; 运算的值是地址,单目
2.’*'表示取内容的预算符,运算对象是地址; 运算的值是内容,单目
3.&*p 与 &a 相同,是地址
*&a 与a相同,是变量
4. (*p)++ 等价于 a++,将 p 所指向的变量值加1
5. *p++ 等价于 *(p++),先对p(地址)进行向下移动,
再取移动后的地址的内容,此时p不再指向a。
eg,变量a = 3 ,则 &a 表示的就是 存放变量a的内容的地址
而 *(&a) 表示的就是对 地址(&a) 取内容,也就是变量a的内容
在这里也就是 3.
void splitfloat(float x, int* intpart, float* fracpart)
{
*intpart = (int)x;
*fracpart = x - *intpart;
}
*可以返回实数部分和小数部分两个值。
4.可以用数组名直接充当函数的实参;
不过对于数值型数组,要记得传入相关的数组长度;
5.在函数中如果对指针进行移动的操作的话,
最好另外设一个指针变量来移动,因为指针
的首地址的保留很重要!
*strcat(连接str2到str1后面)
char *strcat( char *str1, const char *str2 )
{
char *tempstr;
tempstr=str1;
if(!str1||!str2) return NULL;//防止空指针传入。否则程序奔溃。
while(*str1) str1++;
while(*str2) {
*str1=*str2;
str1++;str2++;
}
*str1='\0';
return tempstr; //连接后字符串的的首地址。
}
理解:
1.定义局部变量tempstr来保存首地址,然后对str1和str2直接操作;
2.因为这里是从字符的角度去操作的,所以重新生成的字符串,务必加上结束符!
3.考虑到放置空指针的输入。
4.首字符数组要足够大,否则会达不到效果。
*strcpy(将strSrc复制到strDest)
#include "assert.h"
char *strcpy(char *strDest, const char *strSrc)
{
assert((strDest != NULL) && (strSrc!= NULL));
if (strDest == strSrc)
return strDest;
char *tempDest = strDest; //保存首地址
while((*strDest++ = *strSrc++) != '\0'); //??
*strDest=0;
return tempDest;
}
理解:
1.需要对传入参数strDest和strSrc进行检查,禁止空指针传入。
2.仍然要注意保存好首地址
3.使用const来约束strSrc,提高程序的健壮性。
如果函数体内的语句试图改动strSrc的内容,编译器将指出错误。
4.首字符数组要足够大,否则会达不到效果。
#include "assert.h"
int strcmp(const char *str1,const char *str2)
{ int len = 0;
assert((str1!= NULL) && (str2 != NULL));
while(*str1 && *str2 && (*str1==*str2))
{
str1++;
str2++;
}
if((*str1-*str2)>0)
return 1;
else if((*str1-*str2)<0)
return -1;
else
return 0; }
理解:
1.检查,禁止空指针传入
2.分三种情况进行返回值
*strlen(计算字符串的有效长度)
*返回的值是整型,不包括结束标志,但是会包括换行符,在fgets输入中要注意。
*其他字符串函数:
常用字符串函数的网址:
http://www.runoob.com/cprogramming/c-standard-library-ctype-h.html
http://www.runoob.com/cprogramming/c-standard-library-string-h.html
https://blog.csdn.net/kl28978113/article/details/41549011
strcmp函数的用法:
用于条件设定:(逐一比较元素字符的ASCLL码)
if( strcmp(str1,str2) >0),
if( strcmp(str1,str2) <0),
if( strcmp(str1,str2) ==0),
strcpy函数的用法,比如在(字符串的冒泡排序)这道题目中:
#include
#include
int main()
{
int K;
int N;
char ch[100][11];
char temp[11];
//由于字符串多了一个结束标志,所以记长度要加一
int i, j;
scanf("%d %d", &N, &K);
for (i = 0; i < N; i++)
{
scanf("%s", ch[i]);
//分别对字符数组的每行进行输入;
}
//冒泡排序法:
for (i = 0; i < K; i++)
{
for (j = 0; j < N - 1 - i; j++)
{
if (strcmp(ch[j], ch[j + 1]) > 0)//ch[j]更大
{
strcpy(temp, ch[j]);
strcpy(ch[j], ch[j+1]);
strcpy(ch[j+1], temp);
}
}
}
for (i = 0; i < N; i++)
{
printf("%s\n", ch[i]);
//分别对字符数组的每行进行输出;
}
return 0;
}
从这道题目来看,strcmp函数相当于字符串中的 条件运算符,
而strcpy相当于字符串中的 赋值运算符。
另外也可以定义静态变量,迫使即时是在函数内申请的局部变量
也存储在静态存储区域,使它不因函数进程的结束而消亡。
char* getmonth(int n)
{
静态数组的方法:
static char month[13][15] = { "","January","February","March","April","May","June"
,"July","August","September","October","November","December" };
if (n >= 1 && n <= 12)//n是合法数字;
{
return month[n];
}
else
{
return NULL;
}
}
calloc函数(会自动初始化)
void calloc ( unsigned n, unsigned size)
在内存的动态存储区堆中分配n个连续空间,每一存储空间的长度为size,并且分配后还把存储块里全部初始化为0
若申请成功,则返回一个指向被分配内存空间的起始地址的指针
若申请内存空间不成功,则返回NULL
形式如下:int p = (int ) calloc (n, sizeof(int);
malloc函数(不会自动初始化)
void malloc(unsigned size)
在内存的动态存储区中分配一连续空间,其长度为size
若申请成功,则返回一个指向所分配内存空间的起始地址的指针p,若申请不成功,则返回NULL
**返回值类型:void (),赋给指针要强制转换。
结构体、链表用比较多
形式如下:int p = (int )malloc(nsizeof(int));
*free函数:
void free (void *ptr)
释放由动态存储分配函数申请到的整块内存空间,ptr为指向要释放空间的首地址。
无论是使用malloc函数还是使用calloc函数进行动态申请,
在程序的最后都要记得进行free(p)进行内存空间的释放,
否则会产生内存碎片,影响程序的进程。
int main()
{
//int p[1024][1024];
int (*p)[1024];
int i,j;
p=new int[1024][1024];
for(i=0;i<1024;i++)
for(j=0;j<1024;j++)
p[i][j]=i+j;
for(i=0;i<1024;i++)
for(j=0;j<1024;j++)
printf("i=%d,j=%d,p=%d\n", i,j,p[i][j]);
delete []p;
return 0;
}
*首先指针数组表示的是数组,它的元素是指针,每一个指针可以指向不同的地址
*每一个元素可以指向一个字符串的首地址,这样就可以通过操作指针变量来操作
字符串。
*而二维数组它也是数组,它的元素是一个一个的字符,只不过相当于把它的一整
行当作一条字符串。
*而不同的行当作不同条字符串来处理。
*可以用指针数组的不同元素分别指向二维数组的每一行。
void sort(char a[][11],int n){//注意二维数组的形参的设置
int i,j;
char temp[11];
for(i=0;i0){
strcpy(temp,a[j]);
strcpy(a[j],a[j+1]);
strcpy(a[j+1],temp);}
}}}
*用指针数组:
void fsort(char *color[ ], int n)
{ int k, j;
char *temp;
for(k = 1; k < n; k++)
for(j = 0; j < n-k; j++)
if(strcmp(color[j],color[j+1])>0){
temp = color[j];
color[j] = color[j+1];
color[j+1] = temp;
}
理解:
*由于指针数组内的指针数组可以相互赋值,所以不需要在使用strcpy函数;
*采用动态申请的方式来处理多个字符串:
ppt内的例题:例11-6 输入一些有关颜色的单词,每行一个,以#作为输入结束标志,再按输入的相反次序输出这些单词。
其中单词数小于20,每个单词不超过15个字母(用动态分配内存的方法处理多个字符串的输入)。
样例代码:(不是俺自己写的)
#include
#include
#include
int main(void)
{
int i, n = 0;
char* color[20], str[15];
scanf("%s", str);
while (str[0] != '#') {
color[n] = (char*)malloc(sizeof(char) * (strlen(str) + 1)); /* 动态分配 */
strcpy(color[n], str); /* 将输入的字符串赋值给动态内存单元 */
n++;
scanf("%s", str);
}
for (i = n - 1; i >= 0; i--) {
printf("%s ", color[i]);
free(color[i]);
} /* 释放动态内存单元 */
return 0;
}
指针数组元素可以互相赋值。
//这一点非常重要!!!是指针数组最为灵活的地方。
tmp=color[0];
也可以间接访问操作数组元素所指向的单元内容
printf (“%c”, *(color[0]+1) );
输出的是对应的二维数组第0行列下标为1的字符。
#include
void main( )
{ int i;
char *pcolor[ ] = {“red”, ”blue”,
”yellow”, ”green”, ”black”};
void fsort(char *color[ ], int n);
fsort(pcolor, 5);
for(i = 0; i < 5; i++)
printf("%s ", pcolor[i]);
}
void fsort(char **color, int n)
{ int k, j;
char *temp;
for(k = 1; k < n; k++)
for(j = 0; j < n-k; j++)
if(strcmp(color[j],color[j+1])>0){
temp = color[j];
color[j] = color[j+1];
color[j+1] = temp;
}
}
形式:int (*p)[n]
含义:p为指向含有n个元素的一维数组的指针变量。二级指针。
行指针p是行地址性质的指针。
此时,p+i=a+i ≠ a[i]
(p+i)=(a+i) = a[i]
列指针是一级指针,可以直接把它当成
普通的指针来看就行。
不一样的地方在于它的指向是基于一个已存在的二维数组。
在初始化时,我们需要将它指向这个二维数组某一行
的首地址。
int *p;p=a[0];
*(p+i),表示离a[0][0]第i个位置的元素
while (*sPrt && *sPrt!='\n')//遍历fgets输入的主串;
{
locPrt = sPrt;//标记可能的子串在主串中的位置;
while (*sPrt == *tPrt)//找到相同的元素后开始同时遍历;
sPrt++, tPrt++;
如果找到(指子串指到了'\0'且没有指到'\n'),返回locPtr(地址)
找不到(子串没有遍历到最后);归首子串,继续往下遍历主串;
sPrt++;(遍历主串)
}
最后遍历完主串如果没有找到的话;(有找到会提前跳出函数)
返回NULL;
如何解决长度超过题面MAXS,t在结尾处
小林代码的特点:
主要是运用了数组的特点来查找子串。
小陈代码的特点:
1.有点类似数组的思维,但是本质上使用的是
标准的指针的写法。
2.提供了一种思路,对指针或许没有必要让指针动起来,
可以用一个数字变量来控制控制指针的地址的表达式的
值,从而想控制数组的下标一样地去控制指针,而又不
会像俺的去移动指针的那种方法,需要时刻注意指针的
移动。
3.合理运用了for语句的多重条件的写法。
俺的代码:
1.本质上是对超星视频学习的产物;
2.有一个测试点没有过
3.是采用让指针真实动起来的办法。
void merge(int* a, int m, int* b, int n)
{
先把b数组中的数字全部接到a数组尾部;
然后使用 选择法排序 将他们升序排序;
}
*如果遇到b中最小大于a中最大的话,那直接就相当于把b接到a后;
*有交错的情况,那按这个方法就是从大到下,从后到前进行输入,
特殊的地方是,由于a已经排好序,所以这样进行的过程中,a对a
的操作相当于就是在a数组内进行移动,不会额外侵占有效空间。
小林代码的特点:很好地利用了数组a长度的特点,创新性地采用了从逆向
载入数据的数组重构方式,这样做的话:
1.不需要重新动态申请一个新的数组来分别存放,
2.不需要多层循环嵌套,效率更高。
谈谈俺的代码:
1.能利用好学到的排序方法;
2.没有利用好已知数组已经排好序的特点来展开
3.无法避免在逻辑上使用排序法必须经历的循环
嵌套带来的时间效率上的损失。
这类题目特点的反思:
在题目的已知条件中给出的题设如果含有已经排
好序的特点的话,那比起粗暴地整合和重新排序
更好的方法是对排好序的内容进行移动和重构,
有点类似于数组的插入数据的思路。
动态申请一个大小为(500001*sizeof(char*))字符指针char* sentence用来存放句子;
动态申请一个大小为(250001 * sizeof(int*))整型指针int* loc用来存放大写字母的下标,
用每个大写字母下标位置来表示每个单词的位置;
输入的字符串的有效长度len=strlen(sentence);
从len-1开始逆向用下标法遍历sentence,并将找到的大写字母的下标
按顺序赋值给loc数组;
然后从0到j-1(loc的实际长度-1)遍历loc数组,用loc数组的内容充当sentence的下标,
然后让sentence数组从每个大写字母开始一个一个字符输出直到字符为空格或不是换行符
但是是'\0'为止;
输出完每个单词如果i != j-1(也就是说不是最后一个单词时),都要输出一个空格用来间隔。
动态申请字符指针sentence和输入
void ReverseStr(char* startPtr)
char* endPtr = startPtr;//定义尾部指针;
while (*endPtr && *endPtr != '\n') endPtr++;
//让尾部指针指向尾部,并注意不要指到fgets自带的换行符;
定义用来遍历的指针并让它指向尾部指针减一;//也就是指向最后一个字母字符;
while (Ptr != startPtr)
{
当字符不是空格时开始计算单词长度
当字符遍历到空格时说明完成了一个单词的遍历
用printf("%.*s", len, Ptr)来输出单词;//用一个状态变量
来控制输出格式,即有空格隔开;
}
如果以空格开头,那第一个单词会在前面也就被输出,
如果没有的话,那就要另外输出第一个字符,
并且要注意不是空格开头并且只有一个单词的句子的情况;