C++笔记(四)---零碎基础知识

sizeof与strlen的区别
1)strlen计算字符串的具体长度(只能是字符串),不包括字符串结束符。返回的是字符个数。
2)sizeof计算声明后所占的内存数(字节大小),不是实际长度。
3)sizeof是一个取字节运算符,而strlen是个函数。
4)sizeof可以用类型做参数,strlen只能用char*做参数,且必须以‘\0’结尾,sizeof还可以用函数做参数;
5)数组做sizeof的参数不退化,传递给strlen就退化为指针;

一、异常处理语句

C++中的异常情况: 
语法错误(编译错误):比如变量未定义、括号不匹配、关键字拼写错误等等编译器在编译时能发现的错误,这类错误可以及时被编译器发现,而且可以及时知道出错的位置及原因,方便改正。 
运行时错误:比如数组下标越界、系统内存不足等等。这类错误不易被程序员发现,它能通过编译且能进入运行,但运行时会出错,导致程序崩溃。为了有效处理程序运行时错误,C++中引入异常处理机制来解决此问题。
C++异常处理机制: 
异常处理基本思想:执行一个函数的过程中发现异常,可以不用在本函数内立即进行处理, 而是抛出该异常,让函数的调用者直接或间接处理这个问题。 
C++异常处理机制由3个模块组成:try(检查)、throw(抛出)、catch(捕获) 
抛出异常的语句格式为:throw 表达式;如果try块中程序段发现了异常则抛出异常。 

try 
{ 
    可能抛出异常的语句;(检查) 
} 
catch(类型名[形参名])//捕获特定类型的异常 
{ 
    //处理1; 
} 
catch(类型名[形参名])//捕获特定类型的异常 
{ 
    //处理2; 
} 
catch(…)//捕获所有类型的异常 
{ 
} 

二、函数重载

如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载函数。调用这些函数时编译器会根据传递的实参类型推断想要的是哪个函数。形参无法通过const区分开,而指针引用形参可以:

Record lookup (Phone) ;
Record lookup (const Phone);//重复声明了Record lookup (Phone)
Record lookup (Phone*) 
Record lookup (Phone* const); //重复声明了Record lookup (Phone*)

//对于接受引用或指针的函数来说,对象是常量还是非常量对应的形参不同
//定叉了4个独立的重载函数
Record lookup (Account&) ;//函数作用于Account的引用
Record lookup (const Account&) ;//新函数,作用于常量引用
Record lookup (Account*) ;//新函数,作用于指向Account的指针
Record 1ookup (const Account*) ;//新函数,作用于指向常量的指针

只能把const对象(或指向const的指针)传递给const形参。相反因为非常量可以转换成const,所以上面的4个函数都能作用于非常量对象或者指向非常量对象的指针。不过,传递一个非常量对象或者指向非常量对象的指针时,编译器会优先选用非常量版本的函数。

在C++中同名函数在底层生成的函数名会同时包含返回值类型和参数类型,C语言底层函数名相同,不能实现函数重载。

三、命名空间

编写一些具有名为xyz()函数的代码,并且还有另一个可用的库,它也具有相同的xyz()函数。现在编译器无法知道代码中引用的xyz()函数的哪个版本。命名空间(namespace)被设计来克服这个困难,并被用作额外的信息来区分类似的函数、类、变量等等,它们在不同的库中具有相同的名称。使用名称空间可以定义名称的上下文。本质上名称空间定义了一个范围。

在C语言中只有一个全局作用域,C语言中所有的全局标识共享该作用域,标识符之间可能发生冲突。C++中提出了命名空间的概念,命名空间将全局作用域分成不同的部分,不同命名空间中的标识符可以同名而不会发生冲突。命名空间可以嵌套,全局作用域也叫默认命名空间。

namespace Name
{
    namespace Internal
    {
        /*...*/
    }
    /*...*/
}

可以使用using namespace指令避免使用时前置名称空间。该指令告诉编译器后续代码正在使用指定命名空间中的名称。C++命名空间的使用:

使用整个命名空间:using namespace name;
使用命名空间中的变量:using name::variable
使用默认命名空间中的变量:  ::variable

#include <stdio.h>
#include <iostream>
namespace First
{
    int i= 0;
}
namespace Second
{
    int i= 1;
    namespace Internal //嵌套命名空间
    {
        struct P //嵌套命名空间
        {
            int x;
            int y;
        };
    }
}
int main()
{
    using namespace First; //使用整个命名空间
    using Secnd::Internal::P; //使用嵌套的命名空间
    printf("First::i = %d\n",i);
    printf("Second::i = %d\n", Second::i); //使用命名空间中的变量
    P p={2,3};
    printf("p.x = %d\n", p.x);
    printf("p.y = %d\n", p.y);
    system(" pause");
    return 0;
}

example

#include <iostream>
using namespace std;
// first name space
namespace first_space {
    void func() {
        cout << "Inside first space" << end;
    }
}
// second name space
namespace second_space {
    void func() {
        cout << "Inside second_ space" << endl;
    }
}
using namespace first_space;
int main() {
    // This calls function from first name space.
    func();
    return 0;
}

四、预处理

define

宏定义和函数有何区别?

宏在编译时完成替换,之后被替换的文本参与编译,相当于直接插入了代码,运行时不存在函数调用,执行起来更快;函数调用在运行时需要跳转到具体调用函数。

宏函数属于在结构中插入代码,没有返回值;函数调用具有返回值。

宏函数参数没有类型,不进行类型检查;函数参数具有类型,需要检查类型。

宏函数不要在最后加分号。


宏定义和const区别?

宏替换发生在编译阶段之前,属于文本插入替换;const作用发生于编译过程中。

宏不检查类型;const会检查数据类型。

宏定义的数据没有分配内存空间,只是插入替换掉;const定义的变量只是值不能改变,但要分配内存空间。


宏定义和typedef区别?

宏主要用于定义常量及书写复杂的内容;typedef主要用于定义类型别名。

宏替换发生在编译阶段之前,属于文本插入替换;typedef是编译的一部分。

宏不检查类型;typedef会检查数据类型。

宏不是语句,不在最后加分号;typedef是语句,要加分号标识结束。

typedef char *pStr1;

#define pStr2 char *;

pStr1 s1, s2;

pStr2 s3, s4;
在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char。#define只是简单的字符串替换而typedef则是为一个类型起新名字。


宏定义和内联函数(inline)区别?

在使用时,宏只做简单字符串替换(编译前)。而内联函数可以进行参数类型检查(编译时),且具有返回值。

内联函数本身是函数,强调函数特性,具有重载等功能。

内联函数可以作为某个类的成员函数,这样可以使用类的保护成员和私有成员。而当一个表达式涉及到类保护成员或私有成员时,宏就不能实现了。

条件编译#ifdef, #else, #endif作用?

可以通过加#define,并通过#ifdef来判断,将某些具体模块包括进要编译的内容。

用于子程序前加#define DEBUG用于程序调试。应对硬件的设置(机器类型等)。

条件编译功能if也可实现,但条件编译可以减少被编译语句,从而减少目标程序大小。

宏定义一个取两个数中较大值的功能

#define MAX(x,y)((x>y?)x:y)

printf实现原理?

在C/C++中,对函数参数的扫描是从后向前的。C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来,在计算机的内存中,数据有2块,一块是堆,一块是栈(函数参数及局部变量在这里),而栈是从内存的高地址向低地址生长的,控制生长的就是堆栈指针了,最先压入的参数是在最上面,就是说在所有参数的最后面,最后压入的参数在最下面,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。printf的第一个被找到的参数就是那个字符指针,就是被双引号括起来的那一部分,函数通过判断字符串里控制参数的个数来判断参数个数及数据类型,通过这些就可算出数据需要的堆栈指针的偏移量了。


ifdef  endif

一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。 
条件编译命令最常见的形式为: 
#ifdef 标识符 
程序段1 
#else 
程序段2 
#endif
它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。 
其中#else部分也可以没有,即: 
#ifdef 
程序段1 
#endif
在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件上时,就会出现大量“重定义”错误。在头文件中使用#define、#ifndef、#ifdef、#endif能避免头文件重定义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值