台州网站优化公司,微信自媒体网站建设,国内最好的效果图公司,营销型企业网站建设应遵循的原则上篇文章中说到#xff0c;为了解决C语言会出现人为定义的函数和库函数出现重定义的错误#xff0c;C引入了一个新的概念#xff0c;即命名空间#xff0c;通过认为定义命名空间#xff0c;来解决上述问题。 在本篇文章中#xff0c;将继续介绍C相对于C语言不足来进行的补… 上篇文章中说到为了解决C语言会出现人为定义的函数和库函数出现重定义的错误C引入了一个新的概念即命名空间通过认为定义命名空间来解决上述问题。 在本篇文章中将继续介绍C相对于C语言不足来进行的补充例如缺省参数重载函数等等。
1. 缺省参数
1.1 缺省参数的定义以及单个缺省参数简单应用 缺省参数的定义大致如下缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参。例如下面的函数
void PRINT(int a 0)
{cout a endl;
}int main()
{PRINT(0);PRINT(10);return 0;
} 不难发现相比C语言中的函数书写方法C中对于函数的形式参数给定了一个初始值该值就是上面定义中所说的缺省值。对于这种带有缺省值的函数其调用结果如下 这也证实了定义中的一句话如果没有对函数的实参进行指定赋值则在函数执行时默认采用形参反之采用实参。
1.2 多个缺省参数的应用 在上面的情况中函数只有单一的缺省值。当函数的缺省值数量大于时函数的调用方式会更加多样例如
void PRINT(int a 10, int b 20, int c 30)
{cout a endl;cout b endl;cout c endl;
}int main()
{PRINT();PRINT(1);PRINT(1, 2);PRINT(1, 2, 3);return 0;
} 运行结果为 通过上面的例子可以看到针对这种缺省值数量大于的情况下函数的调用会因为实参的给定而给出不用的效果。 但是对于实参数值的给定需要注意实参的给定必须是连续的中间不能有跳跃的情况例如
PRINT(, 2, );
1.3 全缺省与半缺省 全缺省参数就是上一部分的代码所展示即所有的形参都给定了缺省值。对于半缺省则是对于形式参数不全部给定缺省值例如
void PRINT(int a, int b 20, int c 30)
但是对于半缺省参数的给定需要注意以下两点
1. 缺省值必须从右向左给定且必须是连续的。
2. 当函数的定义和声明分离时缺省参数不能同时在声明和定义中出现。一般是将缺省参数给定到函数声明中 2. 函数重载
2.1 函数重载的定义及类型 函数重载是函数的一种特殊情况C允许在同一作用域中声明几个功能类似的同名函数这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同常用来处理实现功能类似数据类型不同的问题。例如下面给出的例子 int ADD(int num1, int num2)
{cout int ADD(int num1, int num2) ;return num1 num2;
}double ADD(double num1, double num2)
{cout double ADD(double num1, double num2) ;return num1 num2;
}
int main()
{cout ADD(1, 2) endl;cout ADD(1.1, 2.2) endl;
} 在上述代码中建立两个函数且这两个函数函数名相同都是但是这两个函数的返回值类型以及参数类型都不同此时符合上方函数重载的定义构成了函数重载。 对于上述代码运行结果如下 上面的例子展示了函数的类型不同以及其参数类型不同的情况下可以构成重载。当函数的参数数量不同时即
void fun()
{cout fun() endl;
}void fun(int a)
{cout fun(a) endl;
}
int main()
{fun();fun(1);
} 此时符合重载定义中参数数量不同这一特性程序依旧可以正常运行运行结果如下 但是上述函数有一种特殊情况需要注意即将函数的参数给定一个缺省值例如
void fun()
{cout fun() endl;
}void fun(int a 0)
{cout fun(a) endl;
}
int main()
{fun();fun(1);
} 运行程序编译器会报错。这是因为在存在一个不含参数的函数与另一个有参数且参数有缺省值的情况下同时运行两个函数会造成二义性即编译器不能判断在函数中调用函数时调用的函数是上面两个函数哪一个函数。 对于函数重载还有一种方式即类型的顺序不同具体情况如下方代码所示
void fun(int a, char b)
{cout void fun(int a, char b) endl;
}void fun(char a, int b)
{cout void fun(char a, int b) endl;
}
int main()
{fun(2, 1.1);fun(2.2, 1);return 0;
} 3. 引用
3.1 引用的概念以及基本使用 在C中引用是基于C语言中指针的不足为解决这些不足创建的。引用的概念如下引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。 对应代码如下
int main()
{int a 1;int c a;
} 函数中第一行代码创建了一个名为的整型变量第二行则是引用即给已有的整型变量取了一个别名叫做。 在上面的概念中提到编译器不会为引用变量开辟内存空间引用与其引用的变量共用一块内存空间。因此加入对引用进行更改相应的变量也会更改例如对于下面代码
int main()
{int a 1;int c a;cout 更改前 a a endl;cout 更改前 c c endl;c;cout 更改后 a a endl;cout 更改后 c c endl;return 0;
}
运行结果为 引用不光可以用在已有的变量上也可以对引用进行引用即
int main()
{int a 1;int c a;int e c;cout a a endl;cout c c endl;cout e e endl;return 0;
} 运行结果为 对于引用而言其最大的价值适用于做参数例如在之前文章中提到到交换函数
void Swap(int* a, int* b)
{int tmp *a;*a *b;*b tmp;
}
int main()
{int x 5;int y 1;Swap(x, y);printf(%d %d, x, y);return 0;
} 由于函数形参只是实参的一份临时拷贝形参的改变并不会影响实参所以在传递参数时需要传递变量的地址。 如果利用引用的特性即引用的改变会引起变量的改变来实现交换函数则可以一定程度上化简上述函数即
void Swap(int a, int b)
{int tmp a;a b;b tmp;
}
int main()
{int x 5;int y 1;Swap(x, y);printf(%d %d, x, y);return 0;
} 运行结果如下 3.1 使用引用的注意事项
3.1.1 引用与初始化 在指针这部分中如果一个指针只是被创建但是不被初始化则这个指针称为野指针。不过指针的初始化并不是必然的但是对于引用来说必须被初始化例如
int main()
{int a 0;int b;return 0;
}
由于引用没有被初始化因此程序错误。
3.1.2 引用能不能被改变指向 在数据结构关于的链表的文章中如果需要在链表中插入一个新的结点需要改变链表中指针的指向但是对于引用而言不能被改变指向例如
int main()
{int a 0;int b a;int c 1;printf(%p %p %p, a, b, c);printf(\n);b c;cout a a b b c c endl;printf(%p %p %p, a, b, c);return 0;
}
运行结果如下 通过结果不难发现在执行了这一条代码后的地址依旧与的地址相同说明没有改变其指向。只是改变了的值。 因此引用虽然在一定程度上弥补了指针的不足但是由于引用并不能改变指向因此引用并不能完全替代指针。 3.2 引用返回
3.2.1 引用返回的概念
在解释本节标题的内容之前首先需要了解一个概念
int fun()
{int n 0;n;return n;
}
int main()
{int ret fun();return 0;
} 图中给定的程序的函数的返回值返回在函数外部创建了一个名为的变量来接收函数返回值但是由于在函数运行结束后该函数对应的栈帧全部销毁因此并不是直接接收函数的返回值而是返回值先被存到寄存器中通过寄存器来间接接收这个值。 此时如果将函数返回的返回值改为下方代码所示即
int fun()
{int n 0;n;return n;
}
int main()
{int ret fun();return 0;
}
此时再去运行程序编译器会给出警告。这是因为在这种返回类型下返回值并不能确定如果编译器在函数结束后直接清理掉栈帧则返回空如果编译器在函数结束后并不会立即清理掉栈帧则返回还能有返回值。
(注由于VS并不会立刻清理掉栈帧因此运行该程序返回值会返回 通过上面的例子可以得出一个结论即在函数运行结束后即离开函数的作用域后此时的返回对象被销毁因此不能采用引用返回。
但是对于静态变量和利用类似方式创建的变量在函数结束后不会销毁可以采用引用返回。 3.2.2 引用返回的价值 由于用返回的价值主要体现在C的后续内容因此本文在此部分指给出结论原理会在后续的文章中进行阐述。
1. 提高返回速度
2. 可以修改返回对象 3.3 常引用
对于常引用具体可以有下面代码反应
int main()
{const int a 1;int b a;return 0;
} 从上面代码可以看到变量是一个被修饰的常变量下面对变量进行了一次引用。当去运行上述代码时编译器会显示错误。 但是如果将上述代码进行修改即
int main()
{const int a 1;const int b a;return 0;
} 运行此代码时并没有错误不难观察到上下的给的代码唯一的差距就是第二次给出的代码的引用前加了。因此在进行常引用的过程中一定不能涉及变量权限的放大。例如上面运行失败的代码中原本变量的权限是不能修改但是在引用后由于引用的修改会引起原变量的修改因此引用的权限超过了原变量的权限造成了权限放大。所以导致运行错误。这里需要注意的时只是不能放大变量的权限对于变量权限的平移的缩小均无影响例如
int main()
{int c 2;const int d c;return 0;
} 上述代码在引用后缩小了变量的权限可以正常运行。 另外对于一个常量进行常饮用也是允许的即
const int e 10;
令外当引用与被引用的对象存在类型转换的情况时即
int j 10;double h 15;const double k j;
由于类型转化的过程中会额外生成一个常量作用于该过程引用时接收的值并不是而是通过一个常量完成类型转换间接传给由于常量的常性因此需要加上。 4. 指针与引用的练习与区别 1. 引用概念上定义一个变量的别名指针存储一个变量地址。 2. 引用在定义时必须初始化指针没有要求 3. 引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何 一个同类型实体 4. 没有NULL引用但有NULL指针 5. 在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32 位平台下占4个字节) 6. 引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小 7. 有多级指针但是没有多级引用 8. 访问实体方式不同指针需要显式解引用引用编译器自己处理 9. 引用比指针使用起来相对更安全 10.在引用这一小节开头的时候说到引用是针对一个对象起的别名与被引用对象共用一块空间。在汇编层上引用是利用指针来实现的因此也需要开辟空间。不过在日常运用时一般认为引用是不会开辟新空间的。 5. 内联函数 对于一般的函数在函数作用时需要开辟栈帧而内联函数不需要开辟栈帧而是在运行中原地展开。由于内联函数不需要开辟栈帧因此提高了程序的运行效率。 内敛函数的书写只需要在函数声明的开头加上即可例如
inline int Add(int x, int y)
{int z x y;return x y;
}
int main()
{int x 5;int y 1;int ret Add(x, y);return 0;
} 但是需要注意内联函数并不能完全替代普通函数并且当一个函数的代码函数10行时不建议采用内联函数。这是因为内联函数每次调用时都会原地展开当调用的次数较大时例如调用次则一个代码行数为行的内联函数完全展开后为行延缓程序的运行速度。