今天一直在学习函数定义之类的问题,下午正好有机会和大家共享一下.

一、bool 类型

逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节。
声明方法:bool result;result=true;
可以当作整数用(true一般为1,false为0)
把其它类型的值转换为布尔值时,非零值转换为true,零值转换为false,注意会产生截断。

二、const 限定符

(1)、用const给字面常量起个名字(标识符),这个标识符就称为标识符常量;因为标识符常量的声明和应用情势很像变量,所以也称常变量。
定义的一般情势:
const 数据类型 常量名=常量值;数据类型 const 常量名=常量值;
例如:const  float  PI=3.14159f;
注意事项:

常变量在定义时必须初始化;

常变量初始化之后,不答应再被赋值;

正如我在这里所说,其实加了关键字const只是提示编译器这个变量是常量,如果我们在接下来的操纵中试图变动它,编译器会报错,而并不是真正的常量,事实上某些情况下通过指针也是可以变动的(编译器报正告),什么情况下完全不能修改呢,当A是加const限定且初始化的全局变量,此时A位于.rodata段(linux 下)。此外const 用于修饰指针时可以参考这里

(2)、const 与 #define

const定义的常量与#define定义的符号常量的区分:

const定义的常量有类型,而#define定义的没有类型,编译可以对前者停止类型安全检查,而后者仅仅只是做简略替换

const定义的常量在编译时分配内存,而#define定义的常量是在预编译时停止替换,不分配内存。

作用域不同,const定义的常变量的作用域为该变量的作用域范围。而#define定义的常量作用域为它的定义点到程序结束,当然也可以在某个地方用#undef取消

#define定义的常量,容易产生反作用:

//Effective C++ 3rd的一个例子。
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))
int a = 5;
int b = 0;
CALL_WITH_MAX(++a, b);//a被累加二次
CALL_WITH_MAX(++a, b+10);//a被累加一次
在这里,调用f之前,a的递增次数竟然取决于“它被拿来和谁比较”

此外,定义常量还可以用enum,在c++ 中尽量用const、enum替换#define定义常量,用inline 替换带参数的宏定义;但 #define 在底层编程中是必不可少的,下面举个例子:

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

 
#include<iostream>


using 
namespace std;

#define STR(a) #a


#define CAT(a,b) a##b

int main(
void)

{

    
int xy = 
;

    cout << STR(ABCD) << endl; 
// #ABCD => "ABCD"
    cout << CAT(x, y) << endl; 
// x##y => xy

return 
;

}

如果是完全的c++ 菜鸟,这里还得略微解释一下细节,iostream 是c++标准库的一个io流头文件,跟C语言不太一样的是一般没有.h 后缀,using namespace 表示命名空间,简略理解就是统一的函数前缀,类比pthread库如pthread_mutex_init, pthread_mutex_lock 用c++ 方法来表示多是 pthread::mutex::lock。 cout是输出流对象,<<操纵符在C语言中是左移位运算操纵符,在这里被重载成输出操纵符,之所以能并列输出是因为如cout<<xxx 返回的是cout 的引用,当前还会再提。参数宏定义的意义就很清晰了,查看下输出即可。

我们知道printf函数带有可变参数,函数式宏定义也可以带可变参数,同样是在参数列表中用...表示可变参数。例如:

 C++ Code 

1

2

3

4

5

6

 
#define showlist(...) printf(#__VA_ARGS__)


#define report(test, ...) ((test)?printf(#test):\

                                printf(__VA_ARGS__))

showlist(The first, second, 
and third items.);

report(x > y, 
"x is %d but y is %d", x, y);

预处理之后酿成: 
C++ Code 

1

2

 
printf(
"The first, second, and third items.");

((x > y) ? printf(
"x>y") : printf(
"x is %d but y is %d", x, y));

在宏定义中,可变参数的部分用__VA_ARGS__表示,实参中对应...的几个参数可以看成一个参数替换到宏定义中__VA_ARGS__地点的地方。

(三)、结构体对齐

什么是内存对齐

    每日一道理
谁说人与人隔着遥远的重洋,谁说心与心设着坚固的堤防?十六岁的鸟儿飞上天空,总会找到落脚的枝头。

编译器为每一个“数据单元”按排在某个适合的位置上。

C、C++语言非常灵巧,它答应你干涉“内存对齐”

为什么要对齐

性能原因:在对齐的地址上访问数据快。

如何对齐

第一个数据成员放在offset为0的位置

其它成员对齐至min(sizeof(member),#pragma pack(n)所指定的值)的整数倍。

整个结构体也要对齐,结构体总大小对齐至各个成员中最大对齐数的整数倍。

举个例子,

struct test

{
char a;

double b;

char c;

};

根据规则1,a 在位置0;根据规则2,因为VC默认pack为8,可以通过项目-》属性-》c/c++  -》代码生成-》结构体成员对齐选项修改,也可以应用#pragma pack(n) 来修改,#pragma pack() 取消修改,那么b 占据8~15;根据规则2,c在16;现在统共17个字节,根据规则3,结构体总大小需对齐到8的整数倍,即统共是24个字节。

如果将pack 修改成4,则总大小为16。在VC上pack 共有1,2,4,8,16 等5种选择,而linux g++ 则只有1,2,4 可选,默认是4。

(四)、域运算符

C++中增长的作用域标识符 ::

用于对与局部变量同名的全局变量停止访问

用于表示类的成员,当前讲到类的时候再详谈

(五)、new、delete 运算符

(1)、new operator

new运算符可以用于创立堆空间,成功返回顾地址,失败抛出异常
语法:
指针变量=new 数据类型(值);
指针变量=new 数据类型[长度n];
例如:
int *p; p=new int(3);
char *pStr=new char[50];

(2)、delete operator

delete运算符 可以用于释放堆空间
语法:
delete 指针变量;
delete [] 指针变量;
例如:
delete p;
delete [] pStr;

(3)、new 和 delete 执行的步骤

new operator

内存分配(operator new),类似malloc

调用构造函数,讲到类再说

delete operator

调用析构函数,讲到类再说

释放内存(operator delete),类似free

实际上new 有三种用法,包括operator new、new operator、placement new,new operator 包括operator new,而placement new 则没有内存分配而是直接调用构造函数,详细的差异当前再谈。

(六)、函数重载、name managling 与extern "C"

(1)、函数重载

雷同的作用域,如果两个函数名称雷同,而参数不同,我们把它们称为重载overload,函数重载又称为函数的多态性(静态)
函数重载不同情势:

形参数量不同

形参类型不同

形参的顺序不同

形参数量和形参类型都不同

调用重载函数时,编译器通过检查实际参数的个数、类型和顺序来肯定响应的被调用函数。

函数的重载跟函数的覆盖、函数的隐藏是不同的,这一点当前再讲。

合法的重载例子:
int  abs(int i);long abs(long l);double abs(double d);
非法的重载例子:

int  abs(int i); long abs(int i);void abs(int i);
//如果返回类型不同而函数名雷同、形参也雷同,则是不合法的,编译器会报"语法错误"。

(2)、name managling 与extern "C"

name managling这里把它翻译为名字改编,C++为了支撑函数重载,需要将函数名根据参数的不同停止name managling以便区分。
extern “C” 可以实现C与C++混合编程,即对C语言写的函数不停止更名,一般在C的头文件中应用,如果头文件被C代码包括并用C编译器编译,则__cplusplus 没有定义,extern “C" 被略过,如果头文件被C++代码包括并被C++编译器编译,存在__cplusplus 定义故extern "c" 提示编译器不要对 {} 内函数停止更名。

#ifdef __cpluscplus
extern “C”
{
#endif
...
#ifdef __cpluscplus
}
#endif

(七)、带默认形参值的函数

函数声明或者定义的时候,可以给形参赋一些默认值,调用函数时,若没有给出实参,则按指定的默认值停止工作。

*函数没有声明时,在函数定义中指定形参的默认值
*函数既有定义又有声明时,声明时指定后,定义后就不能再指定默认值
*默认值的定义必须遵照从右到左的顺序,如果某个形参没有默认值,则它左边的参数就不能有默认值。
void func1(int a, double b=4.5, int c=3); //合法 
void func1(int a=1, double b, int c=3);  //不合法
*函数调用时,实介入形参按从左到右的顺序停止匹配

* 重载的函数中如果形参带有默认值时,可能产生二义性

 C++ Code 

1

2

3

4

5

6

7

8

 
int add(
int x = 

int y = 
);


int add(
int x = 

int y = 

int z = 
);


int main(
void)

{

    
int sum;

    sum = add(

);

    
return 
;

}

sum=add(10,20)语句产生二义性ambiguity,可以认为该语句是调用第一个函数,也可以是第二个,因此编译器不能肯定调用的是哪一个函数。

参考:

C++ primer 第四版
Effective C++ 3rd
C++编程规范

文章结束给大家分享下程序员的一些笑话语录:

祝大家在以后的日子里. 男生象Oracle般健壮; 女生象win7般漂亮; 桃花运象IE中毒般频繁; 钱包如Gmail容量般壮大, 升职速度赶上微软打补丁 , 追女朋友像木马一样猖獗, 生活像重装电脑后一样幸福, 写程序敲代码和聊天一样有**。

---------------------------------
原创文章 By
函数和定义
---------------------------------

函数定义从零开始学C++之从C到C++(一):const与#define、结构体对齐、函数重载name mangling、new/delete 等的更多相关文章

  1. 《从零开始学Swift》学习笔记(Day 25)——类和结构体定义

    原创文章,欢迎转载.转载请注明:关东升的博客 Swift中的类和结构体定义的语法是非常相似的.类使用class关键词定义类,使用struct关键词定义结构体,它们的语法格式如下: class 类名 { ...

  2. go语言基础之结构体做函数参数 值传递和地址传递

    1.结构体做函数参数值传递 示例: package main //必须有个main包 import "fmt" //定义一个结构体类型 type Student struct { ...

  3. const与#define、结构体对齐、函数重载name mangling、new/delete 等

    一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 声明方式:bool result; result ...

  4. go结构体组合函数

    结构体定义 上面我们说过Go的指针和C的不同,结构体也是一样的.Go是一门删繁就简的语言,一切令人困惑的特性都必须去掉. 简单来讲,Go提供的结构体就是把使用各种数据类型定义的不同变量组合起来的高级数 ...

  5. 深入理解C语言-结构体做函数参数

    结构体做函数参数,在C语言中属于常见现象,此时为了内存考虑,不传递结构体,而是传递结构体的地址 结构体定义 struct Man { char name[64]; int age; }; 结构体可以与 ...

  6. C语言结构体和函数

    #include <stdio.h> struct Person { char *name; }; void change1(struct Person p); void change2( ...

  7. 【C++】结构体/结构体数组/结构体指针/结构体嵌套/函数参数/const

    一.结构体声明 struct Student { //成员列表 string name; int age; int score; }; //s3;定义时直接声明 int main() { struct ...

  8. go语言结构体作为函数参数,采用的是值传递

    经过验证,go语言结构体作为函数参数,采用的是值传递.所以对于大型结构体传参,考虑到值传递的性能损耗,最好能采用指针传递. 验证代码: package main import ( "fmt& ...

  9. golang中结构体当做函数参数或函数返回值都会被拷贝

    1. 结构体做函数的参数或返回值时,都会被重新拷贝一份如果不想拷贝,可以传递结构体指针 package main import "fmt" type Person struct { ...

随机推荐

  1. eclipse的使用

    类似于数据库系统的三层目录结构,一般而言IDE在逻辑上都有三层目录结构:工作空间(或解决方案) -> 工程 -> 文件. 当然,如果构建java project,那么目录结构是可以更深,因 ...

  2. Android 使用库项目时的一个特殊tip

    前提: 项目A作为库项目被项目B引用,但是项目A中有自定义的控件和自定义的属性,当在项目B中使用自定义的属性时,编译时就会直接报错:No resource identifier found for a ...

  3. HTML5之video元素

    一.video元素支持的视频格式 HTML5中的video标签支持3种常用的视频格式: 1.Ogg = 带有Theora 视频编码和Vorbis 音频编码的 Ogg 文件: 2.MPEG4 = 带有H ...

  4. 在linux的shell里访问一个URL

    在linux上访问一个网址有四种方法 1.elinks,用法举例: [weishusheng@centOS6 ~]$ elinks -dump http://www.baidu.com 2. wget ...

  5. .NET/ASP.NET Routing路由(深入解析路由系统架构原理)http://wangqingpei557.blog.51cto.com/1009349/1312422

    阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模型的入口 4.ASP.NET Routing 路由对象模型的内部结构 4 ...

  6. jsp防盗链代码

    // 禁止缓存   response.setHeader("Cache-Control", "no-store");   response.setHeader( ...

  7. Spring 事务管理原理探究

    此处先粘贴出Spring事务需要的配置内容: 1.Spring事务管理器的配置文件: 2.一个普通的JPA框架(此处是mybatis)的配置文件: <bean id="sqlSessi ...

  8. linux常用命令之--文本编辑和文本内容查看命令

    linux的文本编辑和文本内容查看命令 1.文本编辑命令 vi:用于编辑文本文件,基本上可以分为三种模式,分别是一般模式.编辑模式.命令行模式. 一般模式:当编辑一个文件时,刚进入文件就是一般模式. ...

  9. Selenium IDE 测试

    Selenium IDE 测试 调试是为了发现和修复测试脚本,任何脚本开发的共同步骤是错误的处理.为了使这一过程更加稳固,我们可以使用Selenium IDE的一个插件叫“Power Debugger ...

  10. C# 中如何判断某个字符串是否为空的方法 分享了三个方法来判断字

    1. 三种常用的字符串判空串方法:Length法:bool isEmpty = (str.Length == 0);Empty法:bool isEmpty = (str == String.Empty ...