杭州做网站公司哪家好,网站优化seo培,学校网站的建设,工业智能科技网站设计1. 变量/函数的声明和定义的区别#xff1f; #xff08;1#xff09;变量 定义不仅告知编译器变量的类型和名字#xff0c;还会分配内存空间。 int x 10; // 定义并初始化x
int x; //同样是定义 声明只是告诉编译器变量的名字和类型#xff0c;但并不为它分配内存空间…1. 变量/函数的声明和定义的区别 1变量 定义不仅告知编译器变量的类型和名字还会分配内存空间。 int x 10; // 定义并初始化x
int x; //同样是定义 声明只是告诉编译器变量的名字和类型但并不为它分配内存空间。使用extern来修饰。告诉编译器这个变量的定义在其他地方这里使用此变量。 extern int x; // 声明x是一个整型变量2函数 函数定义提供了函数的具体实现它包括了函数的返回类型、函数名、参数类型以及函数体的内容。 int add(int a, int b) {return a b; // 函数体
}函数声明告知编译器函数的返回类型、函数名及其参数类型但并不提供函数的具体实现。 int add(int a, int b); // 声明add函数2. sizeof和strlen的区别 1 sizeof 是一个操作符与、类似而 strlen 是一个库函数在#inlcude string中。 2sizeof 的参数可以是任何数据类型或变量而strlen 只能是以‘ \0 ’结尾的字符串作为参数。 3编译器在编译时就计算出了sizeof的结果而strlen 函数必须要在运行时才会被计算出来其核心是因为strlen 是通过遍历字符串在遇到 ‘\0’ 就会结束结果大小不包含‘\0’而sizeof 则会将其包含在内计算大小。 char str[] Hello;
printf(sizeof(str) %zu\n, sizeof(str)); // 输出6包括\0
printf(strlen(str) %zu\n, strlen(str)); // 输出5不包括\03. 的用法引用和取地址。
1 用作取地址符号表示获取变量的内存地址。
int x 10;
int *ptr x; // 使用 获取 x 的地址并赋给指针 ptrprintf(x 的地址%p\n, (void*)x); // 输出 x 的内存地址
2 用作引用符号表示一个变量的引用即该变量的别名。创建一个引用引用是变量的别名它不会创建新的内存空间而是直接使用原变量的内存。
int x 10;
int ref x; // ref 是 x 的引用两个变量共享相同的内存地址ref 20; // 通过引用修改 x 的值
printf(x %d\n, x); // 输出 x 20引用修改了原变量4. static关键字。 static关键字可以修饰变量和函数对此有着不同给功能。static修饰的变量默认初始化值为0。 变量 1在函数内部定义的变量。 当 static 用于函数内部的局部变量时它表示该变量的生命周期改变。 局部变量通常在每次函数调用时被创建并销毁但使用 static 关键字后局部变量在函数调用结束后不会销毁而是保留其值直到下一次函数调用时继续使用上一次的值。直到程序结束后销毁。 void count_calls() {static int count 0; // 静态变量只初始化一次count;printf(This function has been called %d times.\n, count);
}int main() {count_calls(); // 输出: This function has been called 1 times.count_calls(); // 输出: This function has been called 2 times.return 0;
}2在函数外部定义的变量。 当 static 用于函数外部的变量或函数时它限制了该变量或函数的作用域使得它只能在当前文件中使用无法被其他文件访问。对于被static修饰全局变量来说其他文件无法通过 extern 来引用它。 函数 当 static 用于函数时它限制了该函数的作用域使得它只能在当前文件中使用无法被其他文件访问。即只能在当前的C文件中使用其他文件中无法调用该函数 注意在多线程程序中使用 static 变量时要小心因为它们的值会在多个线程之间共享可能会引发竞态条件。为了保证线程安全通常需要使用同步机制如互斥锁来访问这些静态变量。 5. volatile关键字。 在 C 和 C 中volatile 是一个非常重要的关键字它告诉编译器不要优化该变量的读取或写入操作。例如减少不必要的变量读取或写入以提高程序的效率。但是对于某些变量如硬件寄存器或多线程共享变量编译器优化可能导致程序行为不符合预期。因此volatile 被用来告诉编译器不要对该变量进行优化每次访问该变量时都必须从内存中读取最新的值。 场景对硬件寄存器进行访问时都要加上此关键字。 #define STATUS_REGISTER (volatile int*)0x40001000int main() {int status *STATUS_REGISTER; // 硬件寄存器的读取每次都要从内存重新读取// 其他代码
}6. const关键字。 在 C 和 C 中const 关键字用于声明常量或表示某个对象的值不能被修改。const 提供了一种有效的方式来增强程序的可读性、可维护性以及避免意外的修改。常见的有限制常量、指针、数组、函数参数等的修改性。 1常量 const int x 10; // x 是常量值不能被修改
x 20; // 错误无法修改常量变量 x2指针 常量指针指向常量数据的指针const 放在 * 之前表示指向的数据是常量。 作用即通过这个指针你不能修改它所指向的数据但指针本身可以指向其他内存位置。 const int *ptr x; // ptr 是指向常量 int 的指针不能通过 ptr 修改 x 的值
*ptr 20; // 错误不能通过 ptr 修改值
ptr y; // 合法可以让 ptr 指向其他位置指针常量指的是指针本身是常量const 放在 * 之后表示指针本身是常量。 作用即你不能改变指针指向的地址但指针所指向的数据可以被修改。 int x 5;
int y 10;
int *const ptr x; // ptr 是常量指针指向 x*ptr 20; // 合法可以通过 ptr 修改 x 的值
ptr y; // 错误不能改变 ptr 的值即不能让 ptr 指向 y常量指针指向常量既不允许修改指针的值即指针常量也不允许修改指针所指向的数据即指向常量的数据。 const int *const ptr; // ptr 是常量指针指向常量 int3数组 使用 const 可以确保数组中的元素在程序执行过程中保持不变。 const int arr[] {1, 2, 3, 4}; // arr 中的元素是常量不能修改
arr[0] 10; // 错误不能修改 arr 中的元素4函数参数 在函数参数中使用 const可以确保函数不会意外修改传入的参数特别是对于指针或引用类型的参数。这有助于增加代码的可维护性和安全性。 void print(const int x) {printf(%d, x); // 不能修改 x
}void foo(const int *ptr) {*ptr 10; // 错误不能修改 ptr 指向的数据
}7. inline关键字。 在 C/C 中inline 关键字用于请求编译器将函数的代码插入到调用该函数的地方而不是通过传统的函数调用机制即通过栈保存返回地址、传递参数等。它的目的是提高代码的执行效率特别是对于那些调用频繁且函数体较小的函数。递归函数不能内联。
#include iostreaminline int square(int x) {return x * x;
}int main() {int a 5;int result square(a); // 在这里会将 square(a) 展开为 a * astd::cout result std::endl; // 输出 25return 0;
}编译器会尽可能地将内联函数的代码嵌入到调用点。但是如果内联函数太复杂编译器可能不会进行内联优化尽管我们声明了 inline。编译器有最终决定权可能会忽略 inline 关键字的请求。 8. C中的 malloc 和C中的 new 有什么区别 在 C 和 C 中malloc 和 new 都用于动态内存分配但它们有一些重要的区别。 1new、delete是操作符可以重载只能在C中使用。而 malloc、函数 free是函数在C和C中都可以使用在stdlib头文件中。 2new 在 C 中它不仅分配内存还会调用类的构造函数delete调用类的析构函数如果是类类型的话。而 malloc 和 free 函数仅仅是分配内存和释放内存并不执行构造和析构函数。 3new、delete返回的是某种数据类型的指针而malloc和free返回的是void类型的指针因此需要强制类型转换。 4malloc申请的内存要使用free来释放new申请的内存要使用delete来释放两者不能混用因为底层实现原理不同。 5malloc申请内存失败时会返回NULL所以判断返回值来判断内存是否申请成功。而new申请内存失败时会抛出异常。 int* arr (int*)malloc(10 * sizeof(int)); // 分配 10 个整数的空间
free(arr); // 释放内存int* arr new int[10]; // 分配 10 个整数的数组自动初始化
delete[] arr; // 释放数组自动调用析构函数 9. 程序中的内存分配方式。 1栈区对于所有的局部变量除了局部静态变量都存储在栈区中栈内存的分配和释放由编译器自动管理不需要程序员显式调用。 2堆区使用malloc/new创建的内存都存储在堆区需要程序员手动创建和释放。如果分配的内存使用完成后就必须要记得释放不然会造成内存泄漏的风险 3静态存储区用于存放全局变量和静态变量内存在程序编译时就已经分配好了这块内存在程序整个运行期间都存在。 10. 什么是野指针如何避免 野指针Dangling Pointer是指指向已经被释放或未初始化的内存位置的指针。野指针是指针操作中常见的错误之一它会导致程序崩溃、内存泄漏或者不预期的行为。平时使用时一定要避免野指针的情况。如下所示 // 1. 使用销毁的指针
int *ptr new int(10); // 在堆上分配内存
delete ptr; // 释放内存
// ptr 现在是一个野指针因为它指向已释放的内存//第二种情况
int* createPointer() {int x 10; // 局部变量 xreturn x; // 返回指向 x 的指针
}
int main() {int* p createPointer(); // p 指向局部变量 xprintf(%d\n, *p); // 试图访问已超出作用范围的变量return 0;
}
/* 原因局部变量在函数执行完毕后销毁指针p获取到的是销毁空间的变量会导致崩溃*/// 2. 使用未初始化的指针
int *ptr; // 未初始化的指针
*ptr 10; // 访问未初始化的指针导致未定义行为// 3. 超过作用域。野指针的产生原因及解决办法如下 1指针变量未初始化。解决办法指针声明时初始化可以是具体位置也可以指向NULL。 int *ptr NULL; // C语言中使用NULL初始化2使用被free或delete释放的指针。解决办法指针指向的内存空间被释放后应该指向NULL。 int *p(int *)malloc(sizeof(int));
free(p);pNULL;3指针越界。解决办法在变量的作用域结束前释放掉变量的地址空间并且指向NULL。 11. 什么是函数指针和指针函数 1函数指针 是指向函数的指针。可以通过它调用函数使得程序在运行时能够动态地决定调用哪个函数。这在实现回调函数、函数数组等情况下非常有用。 定义一个函数指针 返回类型 (*指针变量名)(参数类型1, 参数类型2, ...);使用举例 #include iostream// 定义一个普通函数
int add(int a, int b) {return a b;
}int main() {// 声明一个指向函数的指针int (*func_ptr)(int, int);// 将指针指向 add 函数func_ptr add;// 通过函数指针调用函数int result func_ptr(3, 4); // 调用 add(3, 4)std::cout Result: result std::endl; // 输出 7return 0;
}2指针函数 指返回指针的函数。它是一个普通的函数只是返回值类型是一个指针。 返回类型 *函数名(参数类型1, 参数类型2, ...);12. 指针的大小。 在 C/C 中指针的大小是由编译器和系统架构决定的。指针本身的大小与它所指向的数据类型int、double、char 等无关而是与计算机的位数有关。在 32 位系统中指针通常占 4 字节32 位在 64 位系统中指针通常占 8 字节64 位。 13. 内存对齐。 内存对齐指计算机中数据在内存中的存储方式确保数据结构的成员按照特定规则排列以提高访问效率。 1为什么要进行内存对齐 CPU访问内存时如果数据地址是对齐的比如4字节对齐那么访问速度会更快。如果数据没有对齐可能需要多次访问内存甚至导致错误。尤其是在不同的硬件平台上对齐要求可能不同所以编译器会自动进行内存对齐优化。 2对齐规则。 通常每个数据类型的对齐要求是其自身的大小。比如int通常是4字节所以它需要4字节对齐double是8字节需要8字节对齐。结构体的对齐要求则是其成员中最大的对齐值。结构体的总大小需要是对齐值的整数倍所以在成员之间可能会插入填充字节。 3如何减少填充 对于结构体而言调整成员顺序可以优化结构体大小。如下所示 //字节大小为24
struct MyStruct {int a; // 4 字节对齐值 4double b; // 8 字节对齐值 8char c; // 1 字节对齐值 1
};//字节大小为16
struct OptimizedStruct {double b; // 8 字节对齐值 8int a; // 4 字节对齐值 4char c; // 1 字节对齐值 1
}; 14. 结构体和联合体中成员所占内存大小。 1内存分配。 结构体中的每个成员都有自己的内存空间所有成员的内存是按顺序排列的。 联合体中的所有成员共享同一块内存空间。 2内存所占大小。 结构体的总大小是各个成员大小的总和可能会有填充字节以保证字节对齐结构体的对齐方式通常由其最大成员的对齐要求决定。。 联合体无论定义了多少个成员内存大小总是等于其最大成员的大小。 举例 #include iostream
//结构体
struct MyStruct { int a; // 4 bytesdouble b; // 8 byteschar c; // 1 byte
};struct MyStruct2 { int a; // 4 byteschar c; // 1 bytedouble b; // 8 bytes
};//联合体
union MyUnion {int a; // 4 bytesdouble b; // 8 byteschar c; // 1 byte
};int main() {MyStruct s1 {1, 3.14, A};MyStruct2 s2 {1, 3.14, A};MyUnion u{102.2C};std::cout Size of struct: sizeof(s1) bytes std::endl; //大小为24字节。std::cout Size of struct: sizeof(s2) bytes std::endl; //大小为16字节。std::cout Size of union: sizeof(u) bytes std::endl; //大小为8字节。return 0;
}结构体大小分析以使得结构体的总大小是 8最大成员所占字节的倍数。 int a 占用 4 字节。插入 4 字节填充使 double b 从 8 字节边界开始。 double b 占用 8 字节。 char c 占用 1 字节。插入 7 字节填充使结构体总大小为 8 的倍数。 大小4481724。 15. 数组和链表的区别。 1数组的地址空间是连续的而链表的地址空间不是连续的。 2数组大小固定而链表的大小不固定。 3数组的访问速度更快。数组直接可以使用下标进行访问而链表则需要遍历访问。 4链表增删改查的速度更快。 5数组适用于数据量固定或变化不大且需要频繁随机访问的场景。链表适合需要频繁插入和删除不需要随机访问的场景。 16. define和typedef的区别。 1#define 是 C/C 中的预处理指令它在编译之前由预处理器处理进行简单的文本替换。适合定义常量、宏或代码片段但不安全且难以调试。 ●特点不进行类型错误检查只是简单的文本替换。可以定义常量、函数宏或代码片段。 #define PI 3.14159 // 定义常量
#define MAX(a, b) ((a) (b) ? (a) : (b)) // 定义函数宏int main() {double radius 5.0;double area PI * radius * radius; // 替换为 3.14159 * radius * radiusint max_value MAX(10, 20); // 替换为 ((10) (20) ? (10) : (20))return 0;
} 2typedef 是 C/C 中的关键字用于为现有类型定义别名。类型安全且易于调试适合提高代码可读性和维护性。 ●特点进行类型检查是类型安全的。只能用于定义类型别名不能定义常量或宏。 typedef unsigned int uint; // 定义 uint 为 unsigned int 的别名
typedef int* IntPtr; // 定义 IntPtr 为 int* 的别名int main() {uint x 10; // 等价于 unsigned int x 10;IntPtr p x; // 等价于 int* p x;return 0;
} 如果需要类型安全或定义复杂类型别名优先使用 typedef如果需要定义常量或宏函数可以使用 #define。 17. 程序分为几个段 通常程序分为代码段text、数据段data、BSS段、堆heap和栈。具体详情查看本文内容第二章节。 ●代码段存储可执行指令编译后的机器码。 ●数据段通常包括已初始化的全局变量和静态变量。 ●BSS段存放未初始化的全局变量和静态变量或者初始化为0的变量。 ●堆动态分配的内存如 malloc。 ●栈局部变量。 18. 栈和队列的区别 1核心规则 2基本操作 3结构特点 4示例 19. c文件是如何转为可执行文件的
具体详情查看文章Linux环境下的编译和调试。