C语言入门初阶基础一有哪些详细讲解?

2026-04-11 20:561阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计3286个文字,预计阅读时间需要14分钟。

C语言入门初阶基础一有哪些详细讲解?

1. 名称空白+目的:解决C语言的缺陷,命名冲突。

1.命名空间

目的:解决c语言的缺陷,命名冲突。

#include<stdio.h> int rand=0; int main() { printf("%d",rand); }

上面这段程序是可以运行的

但是!

#include<stdio.h> #include<stdlib.h> int rand=0; int main() { printf("%d",rand); }

那么上面的代码就会报错。因为stdlib.h 那么包含了rand这个函数 ,我们重新定义rand 那么出现报错。命名被重复定义了。

1.1命名空间定义一个域

关键字 namespace +空间名

namespace std // namespace name { string a="zhangsan"; }

1.2访问全局域和命名空间作用域

//全局域 int a=0; //命名空间 namespace bit { int a=1; } //局部域 int main() { int a=2; printf("%d\n",::a); //访问的全局域 printf("%d\n",a); //访问局部域2 printf("%d\n",bit::a); //访问命名空间 }

变量搜索顺序:局部域→全局域→展开命名空间or指定访问命名空间域。

展开命名空间 ------→相当于把变量暴露到了全局 ,编译时是否去命名空间搜索

using namespace std; //可以直接使用命名空间的变量不重复

1.3 定义命名空间

命名空间内可以定义函数、变量、结构体、和再嵌套命名空间。

namespace bit { // 命名空间中可以定义变量/函数/类型 int a = 0; int b = 1; int Add(int left, int right) { return left + right; } struct Node { struct Node* next; int val; }; } int main() { // 编译报错:error C2065: “a”: 未声明的标识符 printf("%d\n", a); return 0; }

1.4 命名空间的合并

当我们在多个头文件里面定义了同名的命名空间,当我们调用这些头文件的时候会进行合并两个定义的变量,结构体、函数、嵌套的命名空间,切记不要重复定义变量和函数,可以选择再嵌套一个命名空间来包含这个变量。

1.5 注意事项

using namespace std;

直接展开会有风险,我们定义如果和库重名,就报错了。

建议项目不要直接展开,还是推荐直接指定来访问。

那么问题来了,项目里面我都去指定?

解决办法:把常用展开,只是展开部分

using std::cout; using std::endl;

2.C++输入&输出

<< 叫流插入运算符

叫流提取运算符

cout<<"hello heshiqiang"<<endl; cin>>x;

关键字 :cout cin

特性:自动识别类型 ,可以不用定义类型

作用:输入输出

注意:cin自动识别精度到小数点后一位。 当我们要输入和输出小数点后2位,那么还是乖乖用printf和scanf来控制输入输出。

3.函数重载

c中还是不能进行函数重载。同一作用域下c++中可以重复定义多个同名的函数,但是有一定的要求,如下:

  • 函数名相同,参数类型顺序不同
  • 函数名相同,参数个数不同

#include<iostream> using namespace std; //第一个add int add(int a, int b) { return a + b; } //第二个add void add(double a, double b) { printf("%lf\n", a + b); } int main() { //函数重载 int ret = add(10, 20); printf("%d\n", ret); add(10.0, 20.0); //这里体现的是函数名相同,参数类型顺序不同 //同理 我们可以改变参数个数 一样可以调用同名函数 return 0; }

注意: 改变函数的返回值是无法进行函数重载,必须是上面规定的两条。

那么调用速度会不会变慢?

编译之后,代码变成指令,通过指令找到函数的地址,速度还是很快的,那么如何找到呢,我们打开反汇编指令查看可以发现,c++对函数名进行了修饰,,call找到修饰之后的地址,实现这个函数重载功能。

c++调用函数时候add之后进行了修饰,call指令通过修饰后的地址找到这个函数

那么如何修饰的呢?

第一个add: XXaddii(ii,就是int int)

第二个add:XXadddd(dd,就是double double)

这也是为什么顺序不同,找到的函数会不一样

4.引用

4.1 引用概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

相当于给人取别名

&

1.取地址

例如:scanf("%d",&a);

2.引用

例如:int& k=i;

  • &放到变量前面,那么就是取地址
  • &放到数据类型的后面,那么就是引用

下面就是演示代码

int main() { int a = 10; int& k = a; int b = a; cout << &a << endl; cout << &k << endl; cout << &b << endl; return 0; } //实现结果

可以看到就是a就是k,k就是i。那么a++了,k也就++了,不影响b。

4.1.1引用特性总结


  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体

4.2 多层引用问题

//重新给a取一个别名 这个是合理的 //我们允许一个变量有多个别名 int& m=a; //这个也是被允许的 m是a的别名 那么给m再起一个别名 int& j=m; // x

那么我们可以推广到指针的别名,结构体的别名,函数的别名

4.3使用场景

4.3.1做参数(输出型参数)

1.形参的改变要改变实参

2.提高效率(大对象/深拷贝)

void Swap(int& a,int& b) { int temp=a; a=b; b=temp; }

#include <time.h> #include<iostream> using namespace std; struct A { int a[10000]; }; void TestFunc1(A a) {} void TestFunc2(A& a) {} void TestRefAndValue() { A a; // 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc1(a); size_t end1 = clock(); // 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc2(a); size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; } int main() { TestRefAndValue(); return 0; }

运行截图:

C语言入门初阶基础一有哪些详细讲解?

4.3.2做返回值
  • 常用的传值返回(没有用&)

int add(int a,int b) { return a+b;//返回的时候如果是小量数据那么就会被放到寄存器里面 开一个临时变量 //来存储这个临时拷贝 }

  • 引用返回

注意事项:就是函数的放回,出了作用域,如果返回的对象没有还给系统,那么才能使用引用传值。(如何保持出了定义的函数作用域还不还给系统,那么我们一般定义全局变量)

#include<iostream> using namespace std; int c=0; int& add(int a,int b) { int c=a+b; return c; } int main() { int a=add(10,10); }

  • 在返回大对象的时候,引用返回和常用的传值返回比较

#include <time.h> #include<iostream> using namespace std; struct A{ int a[10000]; }; A a; // 值返回 A TestFunc1() { return a;} // 引用返回 A& TestFunc2(){ return a;} void TestReturnByRefOrValue() { // 以值作为函数的返回值类型 size_t begin1 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc1(); size_t end1 = clock(); // 以引用作为函数的返回值类型 size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc2(); size_t end2 = clock(); // 计算两个函数运算完成之后的时间 cout << "TestFunc1 time:" << end1 - begin1 << endl; cout << "TestFunc2 time:" << end2 - begin2 << endl; } int main() { TestReturnByRefOrValue(); }

运行结果:

通过测试,传值和用引用在效率方面有很大的区别。引用效率更高。

引用返回值的时候权限的改变

int func1() { static int x = 0; return x; } int& func2() { static int x = 0; return x; } int main() { //const int& ret1 = func1(); //权限平移 //int& ret1 = func1(); //权限放大 //int ret1 = func1(); //拷贝 //int& ret2 = func2(); //权限平移 //const int& ret2 = func2(); //权限缩小 被const修饰具有常性不可修改 //出现整型提升 整型转换 就一定会生成一个临时变量来先整型提升或者转换再赋值 double dd=1.11; int ii=dd; const int & rii=dd; //在发生整型提升或者整型转换 都会生成一个临时变量,然后就是把临时变量放到rii里面 // //然后 临时变量具有常性 const修饰之后也有常性 return 0; }

4.4 引用和指针之间的区别

  1. 引用是和原来的实体共用一个空间,相当于还是同一个人,只是不同的名称,不需要开辟空间。
  2. 引用是按照指针的方式实现的。

int main() { int a = 10; //语法层面:不开空间,是对a取别名 int& ra = a; ra = 20; //语法层面:开空间,存储a的地址 int* pa = &a; *pa = 20; return 0; }

上面一段代码的汇编代码对比

我们可以通过观察发现,底层实现原理是一致的。

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全

10.权限不能放大,例如 const int a=0; int& b=a; 这个地方就是错误的

引用总结: 1.、基本任何场景都可以用引用传参。

2、谨慎使用

5.auto关键字特性和使用

我个人认为auto挺好用的,不用自己推断数据类型,编译器自动帮我推断,当写工程的时候,可以避免因为单词拼写错误出现的报错。

5.1定义

auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

5.2auto的特性

1.可以推导初始化的数据类型

2.要想使用auto必须进行初始化

3.可以同行声明多个变量,但是变量数据类型要一致

5.3auto的使用

#include<iostream> using namespace std; int main() { auto a=10; //初始化 并能推断出a的类型为int auto b=10,c=11; //同行定义多个变量 多个变量数据类型是一致的 cout<<a<<" "<<b<<" "<<c<<" "<<endl; return 0; }

int main() { //int a = 0; //int b = a; //auto c = a; //根据右边表达式的类型推导c的类型 //auto d = c + 1.11; //cout << typeid(c).name() << endl; //cout << typeid(d).name() << endl; vector<int> v; int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //简化使用迭代器 auto a = v.begin(); //遍历数组读出数组内容 for (auto i : arr) { cout << i << " " << endl; } //修改数组里面的内容 for (auto& i : arr) { i *= 2; } return 0; }

标签:C入门初阶

本文共计3286个文字,预计阅读时间需要14分钟。

C语言入门初阶基础一有哪些详细讲解?

1. 名称空白+目的:解决C语言的缺陷,命名冲突。

1.命名空间

目的:解决c语言的缺陷,命名冲突。

#include<stdio.h> int rand=0; int main() { printf("%d",rand); }

上面这段程序是可以运行的

但是!

#include<stdio.h> #include<stdlib.h> int rand=0; int main() { printf("%d",rand); }

那么上面的代码就会报错。因为stdlib.h 那么包含了rand这个函数 ,我们重新定义rand 那么出现报错。命名被重复定义了。

1.1命名空间定义一个域

关键字 namespace +空间名

namespace std // namespace name { string a="zhangsan"; }

1.2访问全局域和命名空间作用域

//全局域 int a=0; //命名空间 namespace bit { int a=1; } //局部域 int main() { int a=2; printf("%d\n",::a); //访问的全局域 printf("%d\n",a); //访问局部域2 printf("%d\n",bit::a); //访问命名空间 }

变量搜索顺序:局部域→全局域→展开命名空间or指定访问命名空间域。

展开命名空间 ------→相当于把变量暴露到了全局 ,编译时是否去命名空间搜索

using namespace std; //可以直接使用命名空间的变量不重复

1.3 定义命名空间

命名空间内可以定义函数、变量、结构体、和再嵌套命名空间。

namespace bit { // 命名空间中可以定义变量/函数/类型 int a = 0; int b = 1; int Add(int left, int right) { return left + right; } struct Node { struct Node* next; int val; }; } int main() { // 编译报错:error C2065: “a”: 未声明的标识符 printf("%d\n", a); return 0; }

1.4 命名空间的合并

当我们在多个头文件里面定义了同名的命名空间,当我们调用这些头文件的时候会进行合并两个定义的变量,结构体、函数、嵌套的命名空间,切记不要重复定义变量和函数,可以选择再嵌套一个命名空间来包含这个变量。

1.5 注意事项

using namespace std;

直接展开会有风险,我们定义如果和库重名,就报错了。

建议项目不要直接展开,还是推荐直接指定来访问。

那么问题来了,项目里面我都去指定?

解决办法:把常用展开,只是展开部分

using std::cout; using std::endl;

2.C++输入&输出

<< 叫流插入运算符

叫流提取运算符

cout<<"hello heshiqiang"<<endl; cin>>x;

关键字 :cout cin

特性:自动识别类型 ,可以不用定义类型

作用:输入输出

注意:cin自动识别精度到小数点后一位。 当我们要输入和输出小数点后2位,那么还是乖乖用printf和scanf来控制输入输出。

3.函数重载

c中还是不能进行函数重载。同一作用域下c++中可以重复定义多个同名的函数,但是有一定的要求,如下:

  • 函数名相同,参数类型顺序不同
  • 函数名相同,参数个数不同

#include<iostream> using namespace std; //第一个add int add(int a, int b) { return a + b; } //第二个add void add(double a, double b) { printf("%lf\n", a + b); } int main() { //函数重载 int ret = add(10, 20); printf("%d\n", ret); add(10.0, 20.0); //这里体现的是函数名相同,参数类型顺序不同 //同理 我们可以改变参数个数 一样可以调用同名函数 return 0; }

注意: 改变函数的返回值是无法进行函数重载,必须是上面规定的两条。

那么调用速度会不会变慢?

编译之后,代码变成指令,通过指令找到函数的地址,速度还是很快的,那么如何找到呢,我们打开反汇编指令查看可以发现,c++对函数名进行了修饰,,call找到修饰之后的地址,实现这个函数重载功能。

c++调用函数时候add之后进行了修饰,call指令通过修饰后的地址找到这个函数

那么如何修饰的呢?

第一个add: XXaddii(ii,就是int int)

第二个add:XXadddd(dd,就是double double)

这也是为什么顺序不同,找到的函数会不一样

4.引用

4.1 引用概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

相当于给人取别名

&

1.取地址

例如:scanf("%d",&a);

2.引用

例如:int& k=i;

  • &放到变量前面,那么就是取地址
  • &放到数据类型的后面,那么就是引用

下面就是演示代码

int main() { int a = 10; int& k = a; int b = a; cout << &a << endl; cout << &k << endl; cout << &b << endl; return 0; } //实现结果

可以看到就是a就是k,k就是i。那么a++了,k也就++了,不影响b。

4.1.1引用特性总结


  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体

4.2 多层引用问题

//重新给a取一个别名 这个是合理的 //我们允许一个变量有多个别名 int& m=a; //这个也是被允许的 m是a的别名 那么给m再起一个别名 int& j=m; // x

那么我们可以推广到指针的别名,结构体的别名,函数的别名

4.3使用场景

4.3.1做参数(输出型参数)

1.形参的改变要改变实参

2.提高效率(大对象/深拷贝)

void Swap(int& a,int& b) { int temp=a; a=b; b=temp; }

#include <time.h> #include<iostream> using namespace std; struct A { int a[10000]; }; void TestFunc1(A a) {} void TestFunc2(A& a) {} void TestRefAndValue() { A a; // 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc1(a); size_t end1 = clock(); // 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc2(a); size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; } int main() { TestRefAndValue(); return 0; }

运行截图:

C语言入门初阶基础一有哪些详细讲解?

4.3.2做返回值
  • 常用的传值返回(没有用&)

int add(int a,int b) { return a+b;//返回的时候如果是小量数据那么就会被放到寄存器里面 开一个临时变量 //来存储这个临时拷贝 }

  • 引用返回

注意事项:就是函数的放回,出了作用域,如果返回的对象没有还给系统,那么才能使用引用传值。(如何保持出了定义的函数作用域还不还给系统,那么我们一般定义全局变量)

#include<iostream> using namespace std; int c=0; int& add(int a,int b) { int c=a+b; return c; } int main() { int a=add(10,10); }

  • 在返回大对象的时候,引用返回和常用的传值返回比较

#include <time.h> #include<iostream> using namespace std; struct A{ int a[10000]; }; A a; // 值返回 A TestFunc1() { return a;} // 引用返回 A& TestFunc2(){ return a;} void TestReturnByRefOrValue() { // 以值作为函数的返回值类型 size_t begin1 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc1(); size_t end1 = clock(); // 以引用作为函数的返回值类型 size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc2(); size_t end2 = clock(); // 计算两个函数运算完成之后的时间 cout << "TestFunc1 time:" << end1 - begin1 << endl; cout << "TestFunc2 time:" << end2 - begin2 << endl; } int main() { TestReturnByRefOrValue(); }

运行结果:

通过测试,传值和用引用在效率方面有很大的区别。引用效率更高。

引用返回值的时候权限的改变

int func1() { static int x = 0; return x; } int& func2() { static int x = 0; return x; } int main() { //const int& ret1 = func1(); //权限平移 //int& ret1 = func1(); //权限放大 //int ret1 = func1(); //拷贝 //int& ret2 = func2(); //权限平移 //const int& ret2 = func2(); //权限缩小 被const修饰具有常性不可修改 //出现整型提升 整型转换 就一定会生成一个临时变量来先整型提升或者转换再赋值 double dd=1.11; int ii=dd; const int & rii=dd; //在发生整型提升或者整型转换 都会生成一个临时变量,然后就是把临时变量放到rii里面 // //然后 临时变量具有常性 const修饰之后也有常性 return 0; }

4.4 引用和指针之间的区别

  1. 引用是和原来的实体共用一个空间,相当于还是同一个人,只是不同的名称,不需要开辟空间。
  2. 引用是按照指针的方式实现的。

int main() { int a = 10; //语法层面:不开空间,是对a取别名 int& ra = a; ra = 20; //语法层面:开空间,存储a的地址 int* pa = &a; *pa = 20; return 0; }

上面一段代码的汇编代码对比

我们可以通过观察发现,底层实现原理是一致的。

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全

10.权限不能放大,例如 const int a=0; int& b=a; 这个地方就是错误的

引用总结: 1.、基本任何场景都可以用引用传参。

2、谨慎使用

5.auto关键字特性和使用

我个人认为auto挺好用的,不用自己推断数据类型,编译器自动帮我推断,当写工程的时候,可以避免因为单词拼写错误出现的报错。

5.1定义

auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

5.2auto的特性

1.可以推导初始化的数据类型

2.要想使用auto必须进行初始化

3.可以同行声明多个变量,但是变量数据类型要一致

5.3auto的使用

#include<iostream> using namespace std; int main() { auto a=10; //初始化 并能推断出a的类型为int auto b=10,c=11; //同行定义多个变量 多个变量数据类型是一致的 cout<<a<<" "<<b<<" "<<c<<" "<<endl; return 0; }

int main() { //int a = 0; //int b = a; //auto c = a; //根据右边表达式的类型推导c的类型 //auto d = c + 1.11; //cout << typeid(c).name() << endl; //cout << typeid(d).name() << endl; vector<int> v; int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //简化使用迭代器 auto a = v.begin(); //遍历数组读出数组内容 for (auto i : arr) { cout << i << " " << endl; } //修改数组里面的内容 for (auto& i : arr) { i *= 2; } return 0; }

标签:C入门初阶