C/C++ 学习笔记:字符串、数组相关
来源:互联网 发布:mysql分割字符 编辑:程序博客网 时间:2024/05/22 02:17
- C/C++ 中所有字符串字面值都由编译器自动在末尾添加一个空字符,即默认以 \0 结尾。
eg:字面值 "tang" 实际上是 "tang\0",字面值"tang\0" 实际上是"tang\0\0"
- char 数组
char StrArray[4];// 如果数组 StrArray 不是全局变量,内容是随机的。char StrArray[4] = {'B', 'u'};// 这里会默认给剩余的 2 个元素补上 \0,若 4 个字符全指定了,就不会在最后加上 \0,因为超出数组范围了。char Str[] = {'t', 'a', 'n', 'g'};// 不会以 \0 结尾char Str1[] = "tang";// 因为字符串字面值是以 \0 结尾的,所以数组 Str1 也是以 \0 结尾char Str2[] = "tang\0";// 尽管字面值有一个 \0,编译器还是会默认给字符串字面值后面加一个 \0,即这个字符串常量实际上是 "tang\0\0",所以数组 Str2 实际会以这个字符串常量被默认添加的那个 \0 结尾。所以:sizeof(Str) 为 4;sizeof(Str1) 为 5;sizeof(Str2) 为 6。
- char* 指针
不会自动在末尾补 \0
- string 型变量
1. 以 \0 结尾(不会自动补 \0)2. 其 size() 和 length() 函数得到的都是不含 \0 的长度如果把上面不含 \0 的 Str 数组赋值给 string 型变量,则实际会是一直赋值到 \0 结束。相当于调用 string(constchar*s),然后 Str 退化为指针,然后赋值的时候会一直找到 \0 才结束。
重要的:
- 用字符串初始化指针时,指针指向的是放在常量区的字符串常量;
- 而用字符串来初始化数组时,是用字符串常量的内容初始化数组的内容;
- 数组名是一个右值类型( 比如字符型数组的类型为 char*const ),不能作为左值来接收另一个数组。
1. 数组名指代一种数据结构,这种数据结构就是数组;
2. 数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
char str[10];
str++;// 编译出错,提示 str不是左值
3. 数组名作为函数形参时,沦为普通指针。
void Func( char str[100] ) { cout << sizeof( str ) << endl;// 32位机器下,这里为 4}// Func( char str[100] ) 函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;// 在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
- sizeof 与 strlen 的区别:
1. sizeof(...) 是运算符,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。跟里存储的内容没有关系。
具体而言,当参数分别如下时,sizeof 返回的值表示的含义如下:a) 数组——编译时分配的数组空间大小;b) 指针——存储该指针所用的空间大小(存储该指针的地址的长度,是长整型,应该为 4);c) 类型——该类型所占的空间大小;d) 对象——对象的实际占用空间大小;e) 函数——函数的返回类型所占的空间大小。函数的返回类型不能是 void。
2. strlen(...) 是函数,要在运行时才能计算。参数必须是字符型指针(char* )。
当数组名作为参数传入时,实际上数组就退化成指针了,读到 \0 为止返回长度,而且是不把 \0 计入字符串的长度的。
一些例子:
void test1() { char str[10]; char* str1 = "0123456789"; strcpy( str, str1 );// 字符串 str1 需要 11 个字节才能存放下(包括末尾的 '\0'),而 string 只有 10 个字节的空间,strcpy 会导致数组越界}
void test2() { char str[10], str1[10]; int i; for(i=0; i<10; i++) str1 = 'a';// 编译错误。因为数组名 str1 为 char *const 类型的右值类型,根本不能赋值 // 再者,即使想对数组的第一个元素赋值,也要使用 *str1 = 'a'; strcpy( str, str1 );// 对字符数组赋值后,使用库函数strcpy进行拷贝操作,strcpy会从源地址一直往后拷贝,直到遇到'\0'为止。所以拷贝的长度是不定的。如果一直没有遇到'\0'导致越界访问非法内存,程序就崩了。}
void test3(char* str1) { if(str1 == NULL) return ; char str[10]; if( strlen( str1 ) <= 10 )// 应改为if(strlen(str1) < 10),因为strlen的结果未统计’\0’所占用的1个字节。 strcpy( str, str1 );}
void Test4() { char *str = (char *) malloc( 100 ); strcpy( str, "hello" ); free( str ); ... //省略的其它语句}//在执行 char *str = (char *) malloc(100); 后未进行内存是否申请成功的判断;//另外,在 free(str) 后未置 str 为空,导致可能变成一个“野”指针,应加上:str = NULL;
Swap(int* p1, int* p2) { int *p;// p 没有申请空间就使用,是一个“野”指针,有可能指向系统区,导致程序运行的崩溃 *p = *p1; *p1 = *p2; *p2 = *p;}
在函数中为指针char* pStr 申请空间:
// 传值调用 OKvoid GetMemory( char **p ) { *p = (char *) malloc( 100 );}// 引用调用 OKvoid GetMemory(char *&p) { p = (char *) malloc (100);}// 使用一级指针,不行// 因为作为形参传递后,有p和pStr都指向同一个地方,当malloc后,p就指向了新的地方,而pStr没变void GetMemory( char *p ) { p = (char *) malloc( 100 );}// 在函数里用数组,不行// 数组为函数内的局部自动变量,在函数返回后,内存已经被释放。char *GetMemory( void ) { char p[] = "hello world"; return p;}
写出完整版的 strcpy 函数:
char * strcpy( char *strDest, const char *strSrc ) {// 将源字符串加 const,表明其为输入参数 assert( (strDest != NULL) && (strSrc != NULL) );// 对源地址和目的地址加非 0 断言 char *address = strDest; while( (*strDest++ = * strSrc++) != '\0' ); return address;// 为了实现链式操作,将目的地址返回}
编写类 String 的构造函数、析构函数和赋值函数:
class String {public: String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造函数 ~String(void); // 析构函数 String& operator=(const String &other); // 赋值函数private: char *m_data; // 用于保存字符串};//===============================================================================// 普通构造函数String::String(const char *str) { if(NULL == str) { m_data = new char[1]; // 得分点:对空字符串自动申请存放结束标志'\0'的空 assert(NULL != m_data);// 加分点:对m_data加NULL 判断 *m_data = '\0'; } else { int length = strlen(str); m_data = new char[length+1]; assert(NULL != m_data);// 若能加 NULL 判断则更好 strcpy(m_data, str); }}// String的析构函数String::~String(void) { delete[] m_data; // 或 delete m_data;}// 拷贝构造函数String::String(const String &other) {// 得分点:输入参数为 const 型 int length = strlen(other.m_data); m_data = new char[length+1]; assert(NULL != m_data);// 加分点:对 m_data 加 NULL 判断 strcpy(m_data, other.m_data);}// 赋值函数String& String::operator=(const String &other) { // 得分点:输入参数为const型 if(this == &other) // 得分点:检查自赋值 return *this; delete [] m_data;// 得分点:释放原有的内存资源 int length = strlen( other.m_data ); m_data = new char[length+1]; assert(NULL != m_data);// 加分点:对 m_data 加 NULL 判断 strcpy( m_data, other.m_data ); return *this;// 得分点:返回本对象的引用}
0 0
- C/C++ 学习笔记:字符串、数组相关
- C/C++ 学习笔记:字符串、数组相关
- 【C语言学习笔记】数组、字符串、指针
- C语言学习入门 (四) 字符串、字符串数组、字符和字符串相关函数
- C语言学习入门 (四) 字符串、字符串数组、字符和字符串相关函数
- 【C语言】学习笔记:字符串与字符数组
- C++|C++学习笔记|三、数组、字符串、指针
- 黑马程序员-------c语言学习笔记之数组与字符串
- 学习笔记4-C语言数组和字符串
- C语言学习笔记(二维数组,字符串)
- C语言学习笔记:19_数组-字符数组与字符串(常用字符串函数)
- C语言学习笔记<数组>
- C语言学习笔记:数组
- C string 字符串 学习笔记
- C/C学习笔记/指针与数组
- objective-c学习笔记第九章《objective-c 中数组,字符串,集合》
- C语言笔记-字符串与数组
- 【c/c++】字符串相关操作
- 《黑客与画家》 读书笔记(二)
- Unity读取文件地址分析
- vue.js快速入门
- Android中带签到功能的日历(积分)
- Java集合总结
- C/C++ 学习笔记:字符串、数组相关
- Docker学习笔记(一)基本原理及概念
- C语言基础学习基本数据类型-其他整数类型
- selenium之 浏览器导航栏的三个按钮(back、forward、refresh)
- Class.forName().newInstance()与new
- 报错:ViewPager$LayoutParams cannot be cast to android.widget.AbsListView$LayoutParams
- struts2.5+hibernate5.2整合
- python WSGL的使用
- backtop debug