-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C/C++ 相关 #99
Comments
函数指针定义:指向函数的指针变量。 C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。 函数指针有两个用途:调用函数和做函数的参数。 函数指针类型变量的定义:
例子:
函数指针和指针函数的区别: 函数指针是指一个指向函数入口地址的指针,而指针函数是一个返回值为指针类型的函数。前者是指针,后者是函数。 参考 |
二维数组二维数组本质上是以数组作为数组元素的数组,即“数组的数组”。 二维数组的定义:
二维数组又称为矩阵,行列数相等的矩阵称变方阵。对称矩阵:a[i][j] = a[j][i];对角矩阵:n阶方阵主对角线外都是零元素。 参考 |
char 类型,字符和字符串字符数组实际上是一系列字符的集合,也就是字符串(String)。在C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。
在C语言中,字符串总是以 C语言有两种表示字符串的方法,一种是字符数组,另一种是字符串常量,它们在内存中的存储位置不同,使得字符数组可以读取和修改,而字符串常量只能读取不能修改。
以下面四个字符串为例:
结论是: 因为,string1 和 string2 是指针,为了节省内存,C/C++吧常量字符串放到一个单独的内存区域,所以,当几个指针指向相同的常量字符串时,它们实际上指向的是同一个地址。 参考 |
C/C++ 中的 const指针使用 CONST 的几种情况: (1)指针本身是常量不可变 char* const pContent; (2)指针所指向的内容是常量不可变 const char* pContent; (3)两者都不可变 const char* const pContent; 参考: |
Include 防范和 pragma once1. #include防范在C和C++编程语言中,#include防范,有时被称作宏防范,用于处理#include 指令时,可避免重复引入的问题。在标头档加入#include防范是一种让档案等幂的方法。 例如,
参考: 2. pragma once在C和C++编程语言中,#pragma once是一个非标准但是被广泛支持的前置处理符号,会让所在的文件在一个单独的编译中只被包含一次。以此方式,#pragma once提供类似include防范的目的,但是拥有较少的代码且能避免名称的碰撞。 示例代码:
参考: |
C++ 中的引用类型定义:
引用通常用于函数参数列表和函数返回值。
C++之所以增加引用类型, 主要是把它作为函数参数,以扩充函数传递数据的功能。 C++ 中函数传参有三种形式: (1)将变量名作为实参和形参。这时传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元。// 同 c (2) 传递变量的指针。形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。这种通过形参指针可以改变实参的值。// 同 c (3) C++提供了 传递变量的引用。形参是引用变量,和实参是一个变量,调用函数时,形参(引用变量)指向实参变量单元。这种通过形参引用可以改变实参的值。 引用和指针很相似,但也有所不同:
参考: |
C++ 命名规则类/结构除了异常类等个别情况(不希望用户把该类看作一个普通的、正常的类之情况)外,C++类/结构 的命名应该遵循以下准则:
函数
变量变量应该是程序中使用最多的标识符了,变量的命名规范可能是一套C++命名准则中最重要的部分。 变量的命名变量名由作用域前缀+类型前缀+一个或多个单词组成。为便于界定,每个单词的首字母要大写。对于某些用途简单明了的局部变量,也可以使用简化的方式,如:i, j, k, x, y, z .... 作用域前缀作用域前缀标明一个变量的可见范围。作用域可以有如下几种:
类型前缀类型前缀标明一个变量的类型,可以有如下几种:
推荐的组成形式变量的名字应当使用"名词"或者"形容词+名词"。例如:"nCode", "m_nState","nMaxWidth" .... 常量C++中引入了对常量的支持,常量的命名规则如下:
枚举、联合、typedef枚举、联合及typedef语句都是定义新类型的简单手段,它们的命名规则为: 宏、枚举值宏和枚举值由全大写字母组成,单词间通过下划线来界定,如:ERROR_UNKNOWN, OP_STOP .... 参考: |
内联函数1. 内联函数是什么?内联函数是指用inline关键字修饰的函数。在类内定义的函数被默认成内联函数。内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。 内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理。 2. 为什么需要内联函数?内联扩展是用来消除函数调用时的时间开销。它通常用于频繁执行的函数,对于小内存空间的函数非常受益。 如果没有内联函数,编译器可以决定哪些函数内联 。程序员很少或没有权利控制哪些只能是内联的,哪些不可以内联,作用是程序员可以选择内联的特定应用 。 3. 内联函数与一般函数的区别?内联函数具有一般函数的特性,它与一般函数所不同之处只在于函数调用的处理。一般函数进行调用时,要将程序执行权转到被调用函数中,然后再返回到调用它的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。 4. 内联函数和宏定义的区别宏:
内联函数的功能和预处理宏的功能相似。 宏定义和普通函数的比较: 不过宏也有很多的不尽人意的地方:
内联函数和宏的区别在于,宏是由预处理器对宏进行替代(预编译时),而内联函数是通过编译器控制来实现的(编译时)。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。 通常,在C语言中,内联展开的功能由带参宏(Macros)在源码级实现。内联提供了几个更好的方法:
5. 注意事项(1)内联函数也有一定的局限性,就是函数中的执行代码不能太多了。 所以,在内联函数内不适合用循环语句和 switch 语句。如果内联函数有这些语句,则编译将该函数视同普通函数那样产生函数调用代码,递归函数(自己调用自己的函数)是不能被用来做内联函数的。 内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现。 (3)内联函数只能先定义后使用,否则编译系统也会把它认为是普通函数。 参考 |
构造函数和析构函数
构造函数构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。 析构函数析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。 析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。 参考 |
C++ 动态内存C++ 程序中的内存分为两个部分: 栈:在函数内部声明的所有变量都将占用栈内存。 在 C++ 中,您可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new 运算符。 如果您不需要动态分配内存,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。 为普通数据类型动态分配内存
数组的动态内存分配
对象的动态内存分配delete 与 delete[] 区别针对简单类型 使用 new 分配后的不管是数组还是非数组形式内存空间用两种方式均可。
对于像 int/char/long/int*/struct 等等简单数据类型,由于对象没有 destructor,所以用 delete 和 delete [] 是一样的!但是如果是C++ 对象数组就不同了! malloc() 函数和 new 的区别malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。 参考 |
使用 float 类型处理数据时的精度丢失问题
|
静态全局变量 VS. 全局变量1.全局变量全局变量在整个程序运行时间都会存在,它被存储在内存中的静态存储区。 例如: int num = 10; // 定义一个全局变量
void function() {
printf("%d", num);
} fileB.c extern int num; // 通过 extern 进行引用声明(extern 只是声明,不是定义)
void printNum() {
printf("%d", num);
} 2.静态变量用静态变量来声明一个变量的作用有两个:
下面来具体看看第二种情况: fileA.c static int num = 10; // 定义一个静态的全局变量,因此只能在本文件内部使用
void function() {
printf("%d", num);
} 3. 比较比较以下两种全局变量定义的区别: #import "Person.h"
NSString *key = @"xxxx"; // 外部文件可以通过声明 extern 来使用该变量
@implementation Person
@end #import "Person.h"
static NSString *key = @"xxxx"; // 声明了 static 后,该全局变量只限于本文件使用,而不能被其他文件使用
@implementation Person
@end |
Integer Overflows问题:通过 示例代码: NSArray *nums = @[@1, @2];
// 9223372036854775807
// 二进制:0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
// 十六进制:7fff ffff ffff ffff
// When building 32-bit applications, NSUInteger is a 32-bit unsigned integer.
// A 64-bit application treats NSUInteger as a 64-bit unsigned integer
NSUInteger index = [nums indexOfObject:@0];
NSInteger notFound = NSNotFound; // NSNotFound == NSIntegerMax == LONG_MAX
printf("\n%lx, %lx\n", (unsigned long)index, notFound);
// 9223372036854775807
// 二进制:0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
// 十六进制:7fff ffff ffff ffff
int64_t index64 = index;
printf("\n%llx\n", index64);
// -1
// 二进制: 0000 0000 0000 0000 0000 0000 0000 0001 -> 1111 1111 1111 1111 1111 1111 1111 1111
// 十六进制:ffff ffff
int32_t index32 = (int32_t)index;
printf("\n%x\n", index32);
// 7fffffffffffffff
// 二进制:0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
// 十六进制:7fff ffff ffff ffff
// When building 32-bit applications, NSInteger is a 32-bit integer.
// A 64-bit application treats NSInteger as a 64-bit integer.
NSInteger indexLong = index;
printf("\n%lx\n", (long)indexLong);
// 9223372036854775808
// 二进制: 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
// 十六进制:8000 0000 0000 0000
NSUInteger indexPlusOne64 = index + 1;
printf("\n%lx\n", (unsigned long)indexPlusOne64);
// 0
// 二进制:0000 0000 0000 0000 0000 0000 0000 0000
// 十六进制:0000 0000
int indexPlusOne32 = (int)(index + 1);
printf("\n%x\n", indexPlusOne32); 结论:在编码时,要注意大数问题,避免出现越界/溢出的情况。苹果官方推荐的检查方式是使用 clang 内置的计算检查函数,比如 延伸阅读 |
枚举枚举和宏其实非常类似:宏在预处理阶段将名字替换成对应的值,枚举在编译阶段将名字替换成对应的值。我们可以将枚举理解为编译阶段的宏。 #include <stdio.h>
int main(){
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } day;
scanf("%d", &day);
switch(day){
case 1: puts("Monday"); break;
case 2: puts("Tuesday"); break;
case 3: puts("Wednesday"); break;
case 4: puts("Thursday"); break;
case 5: puts("Friday"); break;
case 6: puts("Saturday"); break;
case 7: puts("Sunday"); break;
default: puts("Error!");
}
return 0;
} Mon、Tues、Wed 这些名字都被替换成了对应的数字。这意味着,Mon、Tues、Wed 等都不是变量,它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是直接被编译到命令里面,放到代码区,所以不能用 参考: |
No description provided.
The text was updated successfully, but these errors were encountered: