1、堆、栈的生长方向
【记忆知识】
栈的生长方向:开口向下(后定义的变量的内存地址更小)
堆的生长方向:开口向上(后分配内存的变量的内存地址更大)
【注意】堆、栈的生长方向和内存存放方向是两个不同的概念,例如:分析a和b的地址大小(栈的生长方向)、a[0]和a[2]的地址的大小(内存存放方向)
int a[10];
int b[10];
答:
a的地址大于b的地址,
但是a[0]的地址小于a[2]的地址。
2、指针铁律
C编译器不允许这样使用!
因为p指向字符串常量“abcdefg”,*p就表示字符串常量“abcdefg”,想用*p对常量“abcdefg”进行修改,肯定会报错误(即写入位置发生冲突)
3、指针的相关知识
函数调用时,可以用n级指针改变n-1级指针的值。
==================================================================================
【字符串操作技巧】
while((*to++ = *from++) != ‘\0’) ; 字符串拷贝
strstr\strchr + while的用法
下面讲解字符串的一些经典的相关操作
4、字符串拷贝
【注】此处不用额外写上 *to = ‘\0’;
//上述代码等价于:
*to = *from;
from++;
to++;
5、siring字符串的相关函数操作【非常重要】
举例:在长字符串中寻找一个短字符串出现的次数。(提示:用函数strstr)
6、两头堵
#include <iostream>
using namespace std;
int strlen_trimSpace1(char *p) //函数1
{//求出除去前后空格的字符串的长度
int count = 0;
int i = 0,j = 0;
j = strlen(p)-1; //记得 -1(减1)
while(isspace(p[i]) && p[i]!='\0') //从前往后
{ //isspace(c)检查参数c是否为空格字符
i++;
}
while(isspace(p[j]) && j>0) //从后往前
{
j--;
}
count = j-i+1;
memcpy(p,p+i,count);
p[count] = '\0';
cout<<p<<endl; //输出修改后的结果p
return 0;
}
int strlen_trimSpace2(char *p,char *q) //函数2
{//求出除去前后空格的字符串的长度
int count = 0;
int i = 0,j = 0;
j = strlen(p)-1; //记得 -1(减1)
while(isspace(p[i]) && p[i]!='\0') //从前往后
{ //isspace(c)检查参数c是否为空格字符
i++;
}
while(isspace(p[j]) && j>0) //从后往前
{
j--;
}
count = j-i+1;
memcpy(q,p+i,count);
q[count] = '\0';
cout<<q<<endl; //为了不修改输入的p字符数组,定义q作为接受数组
return 0;
}
int main()
{
int ret = 0;
//“两头堵”问题
//对于字符串分配内存有三种方式:堆、栈、全局区(常量区)
char input[] = " abcd1111abcd2222abcd333abcd4 "; //除去前后两头空格剩下的字符串必须没有空格
char outstr[100] = " ";
strlen_trimSpace1(input);
//下面运行会崩溃,因为input1是常量,而在strlen_trimSpace函数中对input指向的常量字符串修改,会崩溃
char *input1 = " abcd1111abcd2222abcd333abcd4 ";
//strlen_trimSpace1(input1);
strlen_trimSpace2(input1,outstr);
if(ret!=0)
{
cout<<"strlen_trimSpace Error!"<<endl;
return 0;
}
getchar();
return 0;
}
7、字符串反转
void reserve_str(char *str)
{
int i = 0,j = strlen(str)-1;
char *h = str;
char *r = str+j;
char ch;
while(h < r)
{
ch = *h;
*h = *r;
*r = ch;
h++;
r--;
}
cout<<str<<endl;
}
int main()
{
//char *str = "ABCDEFGHIJKLMN"; //不能用字符串常量,因为不能修改
char str[] = "ABCDEFGHIJKLMN";
reserve_str(str);
getchar();
}
8、字符串合并
char * s1_s2(char* s1,char* s2)
{
//char str[100]; //程序崩溃,因为str是栈中的局部变量,不能返回
char *str = (char*)malloc(sizeof(char)*100); //正确做法:在堆中分配能返回的str指针
char *s = str; //因为str经过两个while循环后被修改,所以用s保存首地址(返回s即可)
char *p1 = s1;
char *p2 = s2;
while(*str++ = *p1++);
str--; //去掉'\0'字符
while(*str++ = *p2++);
return s;
}
int main()
{
char str1[] = "ABCD";
char str2[] = "1234";
char *out = s1_s2(str1,str2);
cout<<out<<endl;
//记得释放在被调函数中分配的堆上的内存
if(out!=NULL)
free(out);
getchar();
}
8、字符串中常见的错误
(0-0)没有给字符变量分配内存就开始使用它(使用方式:strcpy等)
(0-1)越界
(0-2)
(1)字符串和字符数组的区别:是不是带有'\0'.
(2)疑问:字符数组打印时为什么打印出“烫烫烫烫”?
因为printf打印时,没有'\0'结束符,因此打印出乱码。
strcpy等好多字符串操作函数遇见'\0'才结束!
(3)
strlen是一个函数,它不包括'\0'
sizeof是一个运算符,球数据实体的大小
(4)三种给字符串分配内存的内存四区图(栈、堆、全局)
char str1[20] ;
char *str2 = "AAAA";
char *str3 = (char*)malloc(100);
(5) [] 与 * 本质上是一样的。
(6)在程序执行的时候,把字符串指针p的地址给修改了,但是没有察觉,因此使对p进行理所应当的操作时,发生意料之外的结果。
例1:释放a时为什么程序崩溃?
程序解读:开始时a指向分配的字符串的头地址,经过for循环后,a指向字符串末尾。
此时释放a:free(a)是释放了未知的地址,肯定会发生错误,程序崩溃。
【修改】应该讲a指向分配的字符串的头地址再进行释放。
例2:为什么打印不出来to和from?
(7)再进行返回指向字符串的指针时,能否返回出来?
在全局区和堆区分配的可以返回
在栈上分配的不可以返回