C语言特性

C零散知识点

  • 标识符是英文或下划线开头,大小写有区分

  • 符号常量的定义:#define PI 3.14159326,一般用大写字母来区分

  • sighed和Unsighed是表示在内存中第一位表示有无符号,无符号类型整形存放数值扩大一倍bian’lian

  • 赋值运算符两边的数据类型不相同时,会将右边的强制转化为左边的类型

  • 强制类型转换格式(数据类型)变量名

  • 无符号变量不能赋值带有符号的数,否则会输出最大值

  • 可以用格式字符无符号输出输出正数有符号数据,结果没有偏差

  • 在C程序输入数据中,英文的逗号和中文的逗号要输入清楚,英文不会出错,中文会出错且不能用空格代替分隔符

  • double类型的输入不能使用%f进行输入,得用%lf才能正常得到a的值。

  • 而在输出double类型时却可以用%f,这是因为printf(“%f”,a);在执行时C自动将float型的参数转换成double型。

  • 在输入多个数据时,若格式控制串中没有非格式字符作为输入数据之间的间隔,则用空格,Tab或回车键作为分隔符,不能有其他字符如逗号分隔,会被认为是非法字符。例如scanf(“%d %d %d”,&a,&b,&c);,则输入1 2 3。不能1,2,3。但是若有非格式字符作为数据之间的间隔,则必须原样输出。例如:scanf(“%d,%d, %d”,&a,&b,&c);则必须1,2,3.

  • if可以不搭配else作用,但是如果外部嵌套if,else语句时,为避免else配对错误,需用花括号{}将单独if语句包含。或者只需用if语句,可以添加一个空语句的else.

  • C语言和C++,C++是C语言的超集,所以C语言程序可以直在C++中跑。

  • C++的cout和cin是封装了的输入输出。

  • C语言不支持重载,C++支持重载,所以求绝对值函数abs在C++中既可以求整形,又可以求浮点型,而不是C语言的abs和fabs;

\0算字符长度吗?
答:
当你用函数strlen()求某一个字符串的长度时是不包括结尾标志符’\0’的,
但当你用sizeof()求某个字符串占用的内存空间时,结尾字符’\0’是被包括在里面的.也就是说申请空间时需要申请一个字节存储\0.

void test1()
{
 char string[10];
 char* str1 = "0123456789";
 strcpy( string, str1 );
}
  1. str1需要11个大小的字符位置,而string只有10字节大小.
  2. r 以只读方式打开文件,该文件必须存在。

  r+ 以可读写方式打开文件,该文件必须存在。

  rb+ 读写打开一个二进制文件,只允许读写数据。

  rt+ 读写打开一个文本文件,允许读和写。

  w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。

  w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。

  a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)

  a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)

  wb 只写打开或新建一个二进制文件;只允许写数据。

  wb+ 读写打开或建立一个二进制文件,允许读和写。

  wt+ 读写打开或着建立一个文本文件;允许读写。

  at+ 读写打开一个文本文件,允许读或在文本末追加数据。

  ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。

  上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。

  1. char赋值为0,为空字符.
  2. C语言非零值都为真,无论整数还是浮点数还是负数.

静态成员函数访问问题

1.静态函数成员能否用对象访问。

#include
#include
#include
using namespace std;

class People {
protected:
int age;
static int id;
public:
People(int a) :age(a) {
id++;
};
static int getId() {
return id;
}
};
int People::id = 0;
int main() {
People first(1);
cout << first.getId();//验证静态函数成员是否可以通过对象访问:可以。
cout << People::getId();
return 0;
}

运行结果:
image-20211002003748832

结论:

静态函数成员可以通过对象访问。

2.静态函数成员能否访问类的非静态函数成员而不通过对象:
image-20211002003758276

结论:

非静态成员引用必须使用对象调用。

关于构造函数和普通函数的思考

构造函数的特点:

1.使用方法: new fn();

2.构造函数会创建一个新的对象。

3.构造函数中的this指向的实例化的这个对象

4.返回值默认为对象

5.采用大驼峰式命名规则

普通函数的特点:

1.使用方法: fn();

2.不会实例化一个对象;

3.this指向的是调用此方法的对象

4.返回值是return 后面的参数

5.采用小驼峰式命名规则

关于构造函数的命名规则和普通函数的命名规则做一解释:

类名采用大驼峰式书写,函数和变量名采用小驼峰式书写,按理来说构造函数也是函数,应该用小驼峰式命名,但是为什么要用大驼峰式命名呢?因为构造函数要和类名保持相同。但是为什么呢?

下面是我要实现链表的部分代码,假设构造函数采用小驼峰式命名规则如下所示:

template <class ElemType>

struct Node {
    ElemType data;
    Node<ElemType> *next;
    node();
    node(ElemType e, Node<ElemType> * link = NULL);
};

template <class ElemType>
Node<ElemType>::node() {
    next = NULL;
};

template <class ElemType>
    Node<ElemType>::node(ElemType e, Node <ElemType> *link) {
    data = e;
    next = link;
};

Node<ElemType> * p;

那我要实例化一个节点,你说我是用

p = new node<ElemType>();

还是用

p = new Node<ElemType<()

采用第一条语句是因为你构造函数是小驼峰式命名啊,但是new 后面应该跟对象,而node看起来更像是一个函数,在一个应该出现对象的地方你来一个函数是什么鬼?

采用第二条语句是因为new 后面得跟一个对象啊,而Node看起来很直观表示一个对象,但是,你这条语句没在结构体里面定义啊(定义的是node)。

所以,为了解决上述问题,将构造函数统一使用大驼峰式命名规则,以保持实例化时与逻辑思维(new 后面应该跟对象而不是方法)保持一致。

为了贴我原有的代码采用了结构体做例子….虽然我知道类和结构体不一样…….

int *p[5]:和(int *)p[5]区别

*int p[5]:

也就是int(*p)[5].

指向数组的指针,他可以指向一个大小为5的int型数组的首地址.

它也可以用于存储二维数组的行指针.比如一道题的C语言参数为void fun(int **grid, int gridRowSize, int * gridColSizes),最后一个参数就是记录每行的列长度,这样就可以传递一个行列不相等的二维矩阵.

**(int )p[5]:*

指针数组,存储5个int*类型的数组.

32位机器和64位机器各类型所占大小

32位机器:

  • char:1
  • short int:2
  • int:4
  • float:4
  • long:4
  • long long:8
  • double:8
  • 各类指针:4

64位机器:

  • char:1
  • short int:2
  • int:4
  • float:4
  • long:8
  • long long:8
  • double:8
  • 各类指针:8

指针的本质是地址,所以只和机器位数有关,与类型无关.

C Prime Plus

可以用%.2来控制精确到小数点后几位。

C语言中有12个与数据有关的关键字:int long short signed unsigned char float double voif _Bool _Complex _Imaginary.

要保证格式说明符和要显示的数据数量保持一致,否则系统会随机的赋予内存中的垃圾数据给未匹配的格式说明符。

八进制和十六进制的输入输出控制符都是%o和%x/X。但是想要显示八进制和十六进制的前缀则未%#o/x/X。

short int = short 其为简写。short,long是一种有符号类型。如果用long型储存了一个比较小的数,则程序运行速度会变慢。

如果需要将数值教小的值用较大的位数储存,则可用后缀表示,即7LL,7ULL.但是要用后缀所对应的数据类型来存储,比如int = 7ll,还是会将将7用四位(int)来存储.

用%d显示float值并不能强制转换,只是显示垃圾值。

sizeof(),是函数使用正确方式,他以字节为单位给出数据的大小,也就是系统为此数据分配的内存单元的多少,strlen是以字符为单位。

scanf以%s输入字符串时,以空格和回车作为结束的标志,而gets()可以有空格,以回车作为标志.

3.4.3 char

申明:char ch;ch = ‘a’;转义字符的赋值:char zifu = ‘\n’.

C语言将字符常量视为int型而不是char型。

在printf语句中可以直接引用转义字符,例如”printf(”我是 \闫"哈哈”)输出“我是 \闫“哈哈。

3.4.5可移植类型 inttypes.h

可以定义确定位数,最小位数,最大位数,最快位数的数据类型,查表。

3.4.6 浮点型

浮点常量中不要使用空格,例如1.56 e12.

默认情况下,编译器将浮点型常量当作double类型,但floa类型的数据储存为double又减慢程序运行,所哟可以用f或F将浮点常量当作float类型。

3.4.7复数类型(Complex)和虚数类型(Imaginary)

float_Imaginary double_Imaginary

Chapter 4

4.2字符串和格式化输入输出

“x”和’x’都是合法的,不过”x”表示字符串x和\0;而’x’只表示x字符。

string.h文件包含许多与字符有关的库函数。

4.3常量及C预处理器。

#define name shujv

用const可以将变量申明转化为常量申明,使数据不可更改只能使用和显示。

const int a = 3;

limits.h包含了一些常用的符号常量,如果有此头文件,则可以查相关数据类型的最大最小值。

4.4

格式输出修饰符

给定域宽,不足左补零,如果超过了给定域宽,输出区域会调整域宽以恰好能容纳该数值,而不会把该数值截断。

精度控制符对%s的影响:用于%s格式控制符,指定要从相应的字符串中打印的字符个数。当指定的值小于字符串中字符个数时,只输出字符串中的前N个字符(N为精度值),当指定的大于字符串中字符个数时,输出的字符个数为字符串中全部字符的个数。

标识符

​ 标识符只有给定域宽才有意义.

​ 要是空白字符修饰符与+修饰符出现在一起,以+修饰符为准。

​ 浮点数格式来说,#标志符要求小数必须被打印出来,即使小数点之后没有数字,对于%g和%G来说,不会去掉数值尾缀的.00000.

特殊%n

%n与其他格式说明符号不同。不向printf传递格式化信息,而是用于指出函数已经打印的字符的个数,这个数被存储在对应参数(整型指针)所指向的整数中。例如

img

预处理器的范围不能作用到字符串的内部,用*号来巧妙控制域宽和精度.

img

这儿用 * 替换域宽和精度值,使printf()的参数列表里依次出现代表域宽的参数和代表精度值的参数,巧妙的避免的预处理器不能作用于字符串内部的问题。

printf()函数可以返回值,其返回值为打印字符的数目.

C语言中不能在printf函数中用回车来打回车,只能用\n.

scanf,假如它的输入控制符是%d的话,它期望读取一个整数,直到遇到空格或者非数字的字符(不包括+ -),所以scanf函数开始每次读取一个输入字符,它跳过空白字符直到遇见一个非空白字符,最后等到它读取到非数字字符或空格时,将这个非数字字符放回输入,把读取的其他数据放入到变量中,当读取下一个数据时,将从前面所抛弃的那个数据开始读取,如果使用字段宽度修饰符的话,scanf在字段尾部或者第一个非理想字符时停止数据的读取(取决于他俩谁先到);

那如果第一个数据就是非理想数据的话就一直读取后放回再读取,程序就会出错,其他的例如%x,%e有更多的理想数值范围,

但是有一个%s,他会读取除空格外的所有数据,假如数据是先空格再数据再空格,它会跳过空白字符读取数据直到它遇到第二个空白字符,可以对%s使用字符宽度修饰符但不能通过字符宽度企图读入空格以外的数据,在读取数据之后,%s自动添加一个’\0’使之成为一个字符串.

在%d %d之间加入空格和%d%d一样的效果,但是%c不一样,在 %c之前加一个空格表示读取遇到的第一个非空白字符,而%c是读取第一个字符.

printf的*修饰符的作用是你不希望事先自己指出字符宽度而是希望客户输入或者程序指定

表示字符宽度,但是这个也要有参数,例如printf(“%*d”,a,b)

scanf的*修饰符的作用是跳过这个数据,如果程序读取一个文件指定的列会很有用.

Chapter5

数据对象:数据存储区.左值,右值,操作数,运算符.

5.2整数相除得到整数,浮点数相除得到浮点数,整数和浮点数相除自动转换成浮点数,C99要求”趋零截尾”.

赋值运算符=具有右到左的结合性.

5.3sizeof以字节为单位返回操作数的大小

size_t是typedf机制中定义的一个无符号整数类型用于存储sizeof的返回值.

取模运算符的操作数只能是整数,且正负号取决于第一个操作数.

a%b 等价于jian(a/b)*b

自增运算符经常在while()里面结合使用

(x*y)不是一个变量,不能自增自减

5.4副作用是对数据对象和文件的修改.

所有的副作用在顺序点处前全部生效,一个语句就是一个顺序点,一个分号也是一个顺序点,有些逻辑符号也是一个顺序点.

5.5在出现在表达式中,unsigned/signed char/int 都会被自动提升成int,float提升成double;

在有两种不同的数据类型时,会自动转换成较高级别的数据类型进行计算

数据类型从高到低:long double,double,float,unsigned long long,long long,unsigned long,long,unsigned int,int;

数据会自动转换成赋值语句左端的数据类型

在函数参数的传递中,char 和short 会自动转换成int,float会自动转换成double进行数据的传递,但可以通过函数原型来阻止这种行为的发生

Chapter 6

Chapter 7

while(scanf(“%d”,&a)==1),此语句可以在输入为数字的时候执行循环,遇到非数字的时候终止循环。

7.2

if和else之间编译器期待一条语句,如果有多条语句时,需要用花括号转变为复合语句。

getche()语句在得到后不需按回车就进行回显和程序处理,而getchar是收取用户所输入的字符储存到缓存区(先回显),直到用户按回车键后才处理。而getch()语句不回显,并且DEV不能识别getchar函数。

可以用while(ch=getchar()!=’\n’)代替while(ch!=’\n’){ch=getch}

ctype.h头文件包含分析字符的函数。

检查大写字母isupper(),检查小写字母islower(),检查字母isalpha(),检查数字isdigit(),检查字母数字isalnum,空白字符isblack(),除空白字符中的所有所有字符isgraph();标点符号ispunct()等等,以上返回值为真.

映射函数不改变原始参数的值,ctype.h只有两个映射函数:toupper和tolower.,

stdbool.h头文件中包含bool变量,定义形式:bool 变量名;用false和true赋值给bool型变量,这个变量可以运用于判断是否进入循环,如果进入赋值ture,否则赋值faluse.

7.3

iso646.h头文件可将与或非转化位and or not.

C语言不保证复杂式中运算的先后顺序,由一个例外时&&和||,只保证在他俩的前面的式子的值算好后才进行后面式子的运算,也就是C语言保证逻辑表达式是从左向右求值的.这样一个好处是在处理a&&b时,如果a为假则不必判定B的值,而||只有一个为真即为真.

7.5三目运算符

express1?express2:express3,如果表达式一为真的话将表达式2的值赋给表达式1,否则将表达式3的值赋给表达式1.三木运算符可以运用到”不足补一”的问题中,假如刷房子油漆桶购买,cans+=(can%a==0)?0:1;

也可以这样:can==1?’’cans”:”can”.

7.6continue的两个好处:

  1. 减少一级缩进,否则需用if和else

  2. 用作占位符,使程序可读性更强.
    continue的在while循环和do while循环中,continue后的动作时判断循环表达式,而for中则求循环;;中的第三部分

7.7switch的使用

switch()中只能是整型(包括char),case标签只能是整型(包括char)常量或者只包含整数常量的表达式,不能用变量作为case变迁内的内容.

用户输入小写字母还是大写字母不确定时但又需要用作字母做标签时可以用toupper,即switch(toupper(char))

case’A’:……..;

缺点:1.不能判别范围和浮点型;

优点:可以执行的更快并且占据较少代码.

Chapter 10

10.1

采用标识符常量来代表数组的大小

如果不知道自己数据的多少,可以这样:

arr[]={1,2,3,4,5,6}

sizeof(arr)

sizeof(arr)/sizeof(arr[0])表示这个数组的大小,原理是数组的所有字节数除以一个单位的字节数;让系统自动匹配数组大小和初始化列表中的项目的数量.

arr[10]={2,4,[5]=32,45,65,[0]=3},[5]=可以对某个特定的数组元素赋值(第六个元素),45,65将赋给arr[6],arr[7],

并且数组中的元素的值以最后一次赋值为准,例如arr[0]最后的值为=3而不是2;其他没有赋值的元素默认为0;

数组的花括号赋值只能在定义时进行,定义后就不能像这样复赋值了.

数组的大小必须大于零且只能为整型常量,不过C99支持一种变长数组VLA,

10.2

float arr [3] [4]这是定义了一个二维数组,三个”行数组”,这行数组的元素是4个float类型的数组,可以这样赋值

arr [3][4]={
{1,2,3,4},
{2,3,4,5},
{3,4,5,6},
{4,5,6,7},
}

里面的数据也可以省略花括号,只要数据个数正确.

10.3

数组名也是该数组首元素的地址

指针的存储格式是十六进制

指针加一则是对于该指针加一个存储单元

在指针变量前加运算符*就可以取出该指针所存储地址所指向的数据,

注意区分a+1和(a+1)的区别,提示:*的运算级别比+号大

函数声明时,数组名或者指针名可以等级,例如以下四个语句等价

void max(int [],int );

void max(int *,int);

void max(int *a,int n);

void max(int a[], int n);

但是定义函数时不能省略名称;

使用数组作为参数时需要让程序直到数组从哪里开始,从哪里结束,有两种方法,第一种传递数组的起始地址和数组长度给形参

第二种方法传递数组的起始地址和结束地址:max(a,a+num);while(a<a+num),ps:此时a为指针.且a+num是该数组最后一个元素之后的地址.C语言保证数组最后一个元素之后的第一个指针也是合法的.

10.5指针操作

指针有以下几种操作:赋值(将一个地址赋给指针),取值,将一个数加在指针上,增加指针的值,从指针中减去一个数,减小指针的值,求两个指针的差值x,(这个差值x指的是,两个指针之间有x个指针所指向数据类型的数据).

如果ptr指向int类型,ptr++ 就是指向了下一个int字节的地址,ptr+1也是指向了下一个int字节的地址,但前一个ptr变了,后一个ptr没变.

指针比较数组的优点,如果通过值向函数传递数组,则必须再开辟一块存储单元存储数组的内容,而把数组的地址传递给函数的话,就访问更加迅速且节约存储空间.

10.6保护数组的内容

可以用const来保护不希望再程序中被修改的数组或者数据,也可以用const来创建符号常量例如const double PI = 3.1415926,const还可以创建数组常量,指针常量以及指向常量的指针.

const int *a与int *const a,前者指的是只想常量的指针,后者是常量指针.

int arr[]={1,2,3};

const int *a=arr;这两个句子生效后

*(a+1) = 435//不合法,无论是采用数组符号还是指针符号(其实是一个东西)都不能改变arr[1]的值,但是注意到arr本身不是一个常量数组,所以可以改变它本身.

通常把指向常量的指针作为形参,以表明这个函数不会改变数组的数据.

将常量数组和普通数组赋值给指向常量的指针式合法的,但是只有非常量的地址才能赋值给普通的地址(否则就可以通过地址来改变本应该为常量的数据).

因此,在函数的函数参量中使用const,不仅可以保护数据,还更容易接纳const类型的数组

$$const还可以定义常量指针,例如int arr[]={1,2,3},int *const p=a,这样的话这个指针就固定的指向数组开头的地址,但是可以修改数组的数据.


欢迎在评论区中进行批评指正,转载请注明来源,如涉及侵权,请联系作者删除。

×

喜欢就点赞,疼爱就打赏