指针常量、常量指针、常量引用
int * const p //指针常量
int a,b;
int * const p=&a //指针常量
//那么分为一下两种操作
*p=9;//操作成功
p=&b;//操作错误
因为声明了指针常量,说明指针变量不允许修改。如同指针指向一个地址该地址不能被修改,但是该地址里的内容可以被修改。
const int *p=&a //常量指针
//那么分为一下两种操作
*p=9;//操作错误
p=&b;//操作成功
因为常量指针本质是指针,并且这个指针是一个指向常量的指针,指针指向的变量的值不可通过该指针修改,但是指针指向的值可以改变。
常量引用:
double a;
const int &r = a; //正确
const int &r = 10; //正确
关键字inline的作用
inline是C++语言中的一个关键字,用于修饰函数,在编译器编译时将函数调用处直接展开为函数体,从而避免了函数调用的开销,提高程序的运行速度。具体作用如下:
• inline修饰的函数在编译时将被直接展开为函数调用处的代码,从而避免了函数调用的开销,提高程序的运行速度。
• inline函数一般都定义在头文件中,可以被多个源文件调用,不会引起重复定义的错误。
• inline函数不能使用递归调用,也不能包含复杂的循环结构或switch语句等。
内联函数与一般函数的区别
• 内联函数在编译器编译时将函数调用处直接展开为函数体,避免了函数调用的开销,提高程序的运行速度。
• 一般函数在编译器编译时不会展开函数调用,需要在程序运行时进行函数调用,会产生一定的函数调用开销。
内联函数的优缺点和适用场景是什么
内联函数的优点:
• 内联函数可以减少函数调用的开销,提高程序的运行速度。
• 内联函数定义在头文件中,可以方便地被多个源文件调用,不会引起重复定义的错误。
内联函数的缺点:
• 内联函数会增加代码的体积,导致可执行文件的大小增加。
• 内联函数不能包含复杂的循环结构、递归调用等,否则可能会导致代码膨胀。
内联函数适用场景:
• 内联函数适合于被频繁调用、函数体较小、函数参数较简单的函数,可以减少函数调用的开销,提高程序的运行速度。
• 内联函数不适合于包含复杂的循环结构、递归调用等的函数,否则可能会导致代码膨胀,降低程序的运行效率。
内联函数和宏定义的区别
宏定义是预处理器处理的文本替换,而内联函数是编译器处理的类型安全函数。
• 宏定义:宏定义是由预处理器(preprocessor)处理的文本替换。宏定义通常使用指令定义。当预处理器遇到宏定义调用时,它会将宏定义展开,将宏定义直接替换到调用的地方。宏定义的优点是没有函数调用开销,但它也有以下缺点:
1)由于使用宏的时候,只是进行简单的字符替换,不会对类型进行检查,存在安全隐患。
2)由于宏是直接替换的,所以会导致代码稍微长一点。
3)嵌套定义过多可能会影响程序的可读性,会容易出错。
4)边界效应。由于宏定义的时候,其各个分量未加括号,而在使用宏定义的时候,传递的参数是变量的表达式,然后经过系统展开后,由于优先级的原因,导致其结果不是你所希望的.
• 内联函数:内联函数是编译器处理的,使用关键字声明。内联函数的主要目的是建议编译器将函数调用替换为函数体,从而消除函数调用开销。内联函数具有以下优点:
1)inline定义的内联函数,函数代码被放入符号表中,在使用时进行替换(像宏一样展开),效率很高
2)类的内联函数也是函数。编绎器在调用一个内联函数,首先会检查参数问题,保证调用正确,像对待
真正函数一样,消除了隐患及局限性。
3)inline可以作为类的成员函数,也可以使用所在类的保护成员及私有成员。
缺点:
1)内联函数以复制为代价,活动产函数开销
2)如果函数的代码较长,使用内联将消耗过多内存
3)如果函数体内有循环,那么执行函数代码时间比调用开销大。
为什么不能把所有的函数写成内联函数?
• 内联函数展开后会增加代码的体积,导致可执行文件的大小增加,如果定义过多的内联函数会导致程序的运行速度变慢。
• 内联函数不能包含复杂的循环结构、递归调用等,否则可能会导致代码膨胀,降低程序的运行效率。
• 内联函数的展开由编译器决定,不一定能够被成功展开为内联代码。
构造函数、析构函数、虚函数可否声明为内联函数?
• 构造函数和析构函数声明为内联函数 构造函数和析构函数可以声明为内联函数,但是一般不建议这么做。因为构造函数和析构函数需要完成一些复杂的操作,如初始化和清理资源等,定义为内联函数可能会导致代码膨胀,降低程序的运行效率。
• 虚函数声明为内联函数 虚函数可以声明为内联函数,但是虚函数的内联并不是静态绑定的,而是动态绑定的。这就意味着需要在程序运行时才能确定具体调用哪个函数,这可能会影响内联函数的展开效果,导致程序的运行速度变慢。因此,建议将虚函数定义在类的外部,并在函数定义前添加virtual关键字,这样可以避免内联函数的展开问题。
strcpy和memcpy的区别是什么?
• strcpy函数是字符串拷贝函数,用于将一个字符串从源地址拷贝到目标地址。它是以'\0'为字符串结束标志的,会一直拷贝直到遇到'\0'字符为止。
• memcpy函数是内存拷贝函数,用于将指定长度的内存从源地址拷贝到目标地址。它不关心拷贝的内存中是否存在'\0'字符。
strcpy、sprintf与memcpy这三个函数的不同之处
• strcpy函数是字符串拷贝函数,用于将一个字符串从源地址拷贝到目标地址。
• sprintf函数是格式化输出函数,可以将多个参数按照指定的格式输出到一个字符串中。
• memcpy函数是内存拷贝函数,用于将指定长度的内存从源地址拷贝到目标地址。
strcpy函数和strncpy函数的区别?哪个函数更安全?
• strcpy函数是字符串拷贝函数,它将一个字符串从源地址拷贝到目标地址,直到遇到'\0'字符为止。如果源字符串长度超过了目标字符串的长度,会导致目标字符串缓冲区溢出,从而引发安全问题。
• strncpy函数也是字符串拷贝函数,它将一个字符串从源地址拷贝到目标地址,但是不同的是,它最多只会拷贝指定长度的字符,如果源字符串长度超过了目标字符串的长度,那么只会拷贝部分字符,并在目标字符串的末尾自动添加'\0'字符,避免了缓冲区溢出的问题。因此,strncpy函数比strcpy函数更安全。
C语言中memcpy和memmove是一样的吗?
memcpy和memmove都是用于内存拷贝的函数,但是它们的实现方式有所不同。
• memcpy:函数原型:void *memcpy(void *dest, const void *src, size_t n);它从 src 指向的位置开始,复制 n 个字节到 dest 指向的位置。当源和目标内存区域重叠时,memcpy 的行为是未定义的。也就是说,如果你使用 memcpy 复制重叠的内存区域,结果可能是不可预测的。
• memmove:函数原型:void *memmove(void *dest, const void *src, size_t n);它也从 src 指向的位置开始,复制 n 个字节到 dest 指向的位置。与 memcpy 不同的是,memmove 能够处理源和目标内存区域重叠的情况。它确保复制操作在重叠的情况下也能正确进行,不会出现数据损坏的问题。
因此,尽管两者的功能类似,但在源地址和目标地址有重叠的情况下,应该使用memmove函数,而不是memcpy函数。
手写函数:strlen()、strcpy()、strstr()、strcat()、strcmp()、memcpy()
// 计算字符串长度 int strlen(const char* str) { int len = 0; while (*str != '\0') { len++; str++; } return len; } // 拷贝字符串 char* strcpy(char* dest, const char* src) { char* ret = dest; while (*src != '\0') { *dest = *src; dest++; src++; } *dest = '\0'; return ret; } // 在字符串中查找子串 char* strstr(const char* str1, const char* str2) { const char* p1 = str1; const char* p2 = str2; const char* p3 = NULL; while (*p1 != '\0') { p3 = p1; while (*p2 != '\0' && *p1 == *p2) { p1++; p2++; } if (*p2 == '\0') { return (char*)p3; } p1 = p3 + 1; p2 = str2; } return NULL; } // 连接字符串 char* strcat(char* dest, const char* src) { char* ret = dest; while (*dest != '\0') { dest++; } while (*src != '\0') { *dest = *src; dest++; src++; } *dest = '\0'; return ret; } // 比较字符串 int strcmp(const char* str1, const char* str2) { while (*str1 == *str2 && *str1 != '\0') { str1++; str2++; } return (*str1 - *str2); } // 内存拷贝 void* memcpy(void* dest, const void* src, size_t n) { char* p_dest = (char*)dest; const char* p_src = (const char*)src; while (n--) { *p_dest++ = *p_src++; } return dest; }