C和C++的差异:可变长数组、常量、类型转换、字符串等

目录

一,可变长数组

二,伸缩型数组

三,常量

1,字面值和只读变量、数组长度

2,const、运行期常量、编译期常量

3,constexpr、常量表达式、CTFE

四,结构体

1,成员

2,继承

3,定义方式

(1)C语言定义结构体

(2)C++定义结构体

五,auto

六,类型转换

1,隐式类型转换

(1)C语言隐式类型转换

(2)C++隐式类型转换

2,强制类型转换

3,类型转换去const

4,强制类型转换运算符

七,内联函数

1,内联函数

2,内联的优缺点

3,C/C++内联

八,函数原型

九,字符串

1,字符

2,C语言字符串

(1)字符串的表示

(2)输入输出

(3)常用函数

3,string类

(1)定义,初始化,输入输出

(2)string的赋值、计算长度、翻转

(3)从string中取字符

(4)string的比较、连接

(5)string转字符数组

(6)string中查找字符


一,可变长数组

C99开始支持可变长数组:

void testf()
{
    int n;
    scanf("%d",&n);
    int arr[n];
    arr[3]=45;
    printf("%d",arr[3]);
}

C++不支持,这是唯一C++没有的C语言的语法。

可变长数组只能是局部变量,不能是全局变量

然而,一些编译器让C++也支持可变长数组,我推测应该是直接按照C语言中的逻辑进行编译。

二,伸缩型数组

C和C++都支持

typedef struct
{
    int a;
    char data[];
}st;

或者

typedef struct
{
    int a;
    char data[0];
}st;

第一种写法是C语言标准语法,第二种叫struct hack,不属于标准语法。

数组data是不占内存的,实际能分配多少内存,取决于给这个结构体分配多少内存,data说白了就是指向结构体的尾巴后面一个字节的指针。

相关文章:结构体大小和对齐值_nameofcsdn的博客-CSDN博客

三,常量

1,字面值和只读变量、数组长度

在C++中,常量包括字面值和只读变量

int a=12345;
const int b=123;

这里,12345是字面值,a是变量,b是只读变量。

12345是常量,b是常量,a不是常量。

在C中,常量只包括字面值,只读变量不是常量。

上述代码在C语言的解读是,12345是常量,b不是常量。

C和C++中,定义数组的大小都只能用常量

所以C++有2种定义数组的方法:

const int b=123;
int c[123];
int d[b];

这2种写法都是正确的。

但是在C语言中,上述d数组如果是全局变量就是非法的,如果是局部变量那就是可变长数组

2,const、运行期常量、编译期常量

关键字const叫做限定符,因为它限定了声明的含义。

const变量是区分编译期常量和运行期常量

const int b=123;
int d[b];

这是的b编译期常量

int f(int a)
{
    const int b=a;
    int d[b];
    cout<<sizeof(d)/sizeof(int);
    return 0;
}

这里的b是运行期常量,它的值在编译期是未知的,数组d是可变长数组。

于是我在网上找到了另外一种代码,用array检测一个变量是不是常量:

#include<iostream>
#include <array>
using namespace std;

int main()
{
	int a = 10;
	const int b = a;
	const int c = 10;
	//array<int,b>arr;
	array<int, c>arr2;
	return 0;
}

用运行期常量给array对象分配空间是不行的,必须用编译期常量才行。

总之,const变量是区分编译期常量和运行期常量的,,而字面值当然都是编译期常量。

3,constexpr、常量表达式、CTFE

在上面的例子中,同样是const常量,有的是编译期常量,有的是运行期常量。

于是C++新增了constexpr关键字,对于编译期常量可以直接用constexpr来修饰。

#include<iostream>
#include <array>
using namespace std;

int main()
{
	int a = 10;
	const int b = a;
	constexpr int c = 10;
	//array<int,b>arr;
	array<int, c>arr2;
	return 0;
}

constexpr还可以用于CTFE,即Compile Time Function Execution

先看如下代码:

#include<iostream>
#include <array>
using namespace std;

const int f(int a)
{
    return a*a;
}

int main()
{
    // array<int,f(5)>arr;
    return 0;
}

编译不过是因为,f(5)是运行期常量,而array需要的是编译期常量

把const换成constexpr之后:

#include<iostream>
#include <array>
using namespace std;

constexpr int f(int a)
{
    return a*a;
}

int main()
{
    array<int,f(5)>arr; //5是字面值
    int a=5;
    //array<int,f(a)>arr2; //a是变量
    const int b=5;
    array<int,f(b)>arr3; //b是编译期常量
    const int c=a;
    //array<int,f(c)>arr4; //c是运行期常量
    return 0;
}

这样,5和b是编译期常量,所以f(5)和f(b)是编译期常量,而f(a)和f(c)都不是编译期常量,所以不能用来分配array对象。

总之,constexpr的作用是,在编译期运行函数,在输入编译期常量的条件下,可以返回编译期常量。

四,结构体

1,成员

(1)C++允许有内部成员函数,且允许该函数是虚函数,C的结构体内不允许有函数存在,但是可以有函数指针。所以C的结构体是没有构造函数、析构函数、和this指针的。

(2)C的结构体对内部成员变量的访问权限只能是public,而C++允许public,protected,private三种。

2,继承

C语言的结构体是不可以继承的,C++的结构体是可以从其他的结构体或者类继承过来的。

3,定义方式

(1)C语言定义结构体

(1)不带typedef

一种创建实例的写法:

struct A{
    int x;
};

struct A a;

(2)先定义结构体,再typedef

同名定义,两种创建实例的写法:

struct A{
    int x;
};
typedef struct A A;

struct A a;
A a2;

不同名定义,两种创建实例的写法:

struct A{
    int x;
};
typedef struct A B;

struct A a;
B a2;

(3)typedef 加 结构体定义

同名定义,两种创建实例的写法:

typedef struct A{
    int x;
}A;

struct A a;
A a2;

不同名定义,两种创建实例的写法:

typedef struct A{
    int x;
}B;

struct A a;
B a2;

本质上,先定义结构体再typedef,和typedef 加 结构体定义,并没有什么区别。

(4)typedef匿名结构体

一种创建实例的写法:

typedef struct{
    int x;
}A;

A a;

(2)C++定义结构体

C++除了可以使用上述C语言能使用的所有写法之外,还有3种额外写法:

(1)不带typedef

struct可以省略

struct A{
    int x;
};

A a;

(2)结构体定义并创建实例,不同名

struct A{
    int x;
}a;

int main()
{
    struct A b;
    A c;
    a.x=12345,b.x=1234,c.x=123;
    cout<<a.x<<"  "<<b.x<<"  "<<c.x;
    return 0;
}

输出:12345  1234  123

(3)结构体定义并创建实例,同名

struct A{
    int x;
}A;

int main()
{
    struct A a;
    a.x=12345,A.x=123;
    cout<<a.x<<"  "<<A.x;
    return 0;
}

输出:12345  123

五,auto

C语言中,auto表示自动变量,修饰的是存储类别,参考C语言存储类别、存储区-CSDN博客

C++中,auto表示类型推导,修饰的变量类型。

六,类型转换

1,隐式类型转换

C/C++都有,包括赋值语句转换、初始化时的转换、表达式中的转换、传参时的转换

(1)C语言隐式类型转换

1.当类型转换出现在表达式时,无论是unsigned还是signed的char和short都会被自动转换成int,如有必要会被转换成unsigned int(如果short与int的大小相同,unsigned short就比int大。这种情况下,unsigned short会被转换成unsigned int)。

在K&R那时的C中,float会被自动转换成double(目前的C不是这样)。由于都是从较小类型转换为较大类型,所以这些转换被称为升级。

2.涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。

3.类型的级别从高至低依次是long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。

例外的情况是,当long 和 int 的大小相同时,unsigned int比long的级别高。

4.在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能导致类型升级或降级。

5.当作为函数参数传递时,char和short被转换成int,float被转换成double

 C语言基本数据类型转换C语言其他基础_c++ opencv读入图像int转double-CSDN博客

(2)C++隐式类型转换

Ranking of implicit conversion sequences

1) Exact match: no conversion required, lvalue-to-rvalue conversion, qualification conversion, function pointer conversion, (since C++17) user-defined conversion of class type to the same class

2) Promotion: integral promotion, floating-point promotion

3) Conversion: integral conversion, floating-point conversion, floating-integral conversion, pointer conversion, pointer-to-member conversion, boolean conversion, user-defined conversion of a derived class to its base

精确匹配、提升、转换 三种隐式类型转换(包括里面的各小项)优先级依次降低。

由构造函数完成的隐式转换:

class A {
public:
	A(int x)
	{
		this->x = x * x;
	}
	int x;
};

int f(A a)
{
	return a.x + 1;
}

int main()
{
	cout << f(3) << " ";
	return 0;
}

输出10

相当于:

int main()
{
	A b = 3;
	cout << f(b) << " ";
	return 0;
}

而如果是这样:

int f(A a)
{
	return a.x + 1;
}

int f(double a)
{
	return a + 2;
}

int main()
{
	cout << f(3) << " ";
	return 0;
}

则输出5,因为int转double的优先级高一些。

2,强制类型转换

C/C++都有,写法略有区别

C风格:(int)x

C++风格:int(x)

by the way, (int)(x) 这种写法也是OK的

3,类型转换去const

c语言的const入参毫无约束力:

int f(const int*pp)
{
    int *p=pp;
    *p=3;
    return 0;
}

C++要稍微好一点:

int f(const int*pp)
{
    int *p=(int *)pp;
    *p=3;
    return 0;
}

4,强制类型转换运算符

C语言里面只有(int)这种形式的强制类型转换运算符,没有专用的强制类型转换运算符。

C++有4个专用的强制类型转换运算符:

dynamic_cast
const_cast
static_cast
reinterpret_cast

发明这4个运算符,是为了严格限制使用场景,使得用法更规范,避免一些不容易发现的错误。

(1)dynamic_cast


(2)const_cast


(3)static_cast


(4)reinterpret_cast

七,内联函数

1,内联函数

内联函数的声明方式是在函数声明或者实现处用inline修饰

#include<iostream>
using namespace std;

inline int f(int x)
{
    return x*x;
}

int main()
{
    int x;
    cin>>x;
    cout<<f(x);
    return 0;
}

inline的作用是,建议编译器内联。

如果函数体太复杂,太大,或有递归,或者其他特殊原因,编译器会拒绝内联。

2,内联的优缺点

内联就是把指令展开,这个函数体就不存在了,编译之后没有这个符号了。

优点是执行速度快一些,

缺点是代码内存大一些,而且无法直接对这个函数进行打补丁。

3,C/C++内联

单纯的inline,C/C++的语法和细节应该是一样的,

但是如果内联和static、extern等组合起来就比较复杂,C/C++的细节会有所不同。

八,函数原型

 

九,字符串

1,字符

类型:char

输入:getchar

输出:putchar

示例:

#include <stdio.h>

void main()
{
	char ch;
	while (ch = getchar())
	{
		if (ch == EOF)break;
		printf("%d", ch);
		putchar(ch);
	}
	return;
}

运行结果:(8行)

a^Zb
97a
26
a
97a
10

^Z

解释:

输入a^Zb并回车,输入了2个字符a和^Z,再输入a并回车,输入了2个字符a和换行符,再输入^Z程序结束

注1:

'\0'的ASCII码是0,‘^A’ - '^Z' 的ASCII码是1-26,其中换行符'\n'的是10,'^Z'的是26,但是当输入缓冲区为空时'^Z'出现在'\n'之后时,'^Z'的ASCII码是-1,表示文件结尾end of file

注2:

EOF是常量-1,OJ输入输出经常用EOF来控制。

注3:

10号'\n'和26号'^Z'都是getchar的截断,输入一行字符串,getchar只能读取到第一个'\n'或'^Z',后面如果还有就丢弃

2,C语言字符串

(1)字符串的表示

c语言中,字符串用char数组表示,字符串末尾加上'\0'表示结束

(2)输入输出

输入:gets

输出:puts

示例:

#include <stdio.h>
#include<string.h>

void main()
{
	char str[]="abcde";
	puts(str);
	gets(str);
	printf("%d\n", strlen(str));
	puts(str);
	return;
}

运行结果1:(4行)

abcde
^Za
5
abcde

运行结果2:(5行)
 

abcde
a^Zb

2
a?

可以看出,换行符和^Z都是gets函数的截断,区别在于

换行符会被gets读取并丢弃,而gets对^Z的处理和getchar一样,缓冲区为空时就是-1号字符,缓冲区不为空时就是26号字符

(3)常用函数

(1)strcat函数——字符串连接

string catenate字符串连接

示例:

#include <stdio.h>
#include<string.h>

void main()
{
	char str1[10] = "ab";
	char str2[] = "cd";
	printf("%d %d\n", strlen(str1), strlen(str2));
	strcat(str1, str2);
	printf("%d %d\n", strlen(str1), strlen(str2));
	printf("%s %s", str1, str2);
	return;
}

运行结果:

2 2
4 2
abcd cd

strcat(str1, str2)函数就是把str2复制到str1后面

(2)strcpy和strncpy函数——字符串复制

strcpy(str1,str2)把str2复制到str1的位置

strncpy(str1,str2,n)把str2的前n个字符复制到str1的位置

(3)strcmp函数——字符串比较

strcmp(str1,str2)是按照字典序比较,返回值>0,=0,<0分别表示str1>str2,str1==str2,str1<str2

(4)strlen和sizeof——字符串长度

sizeof是关键字,计算的是数组长度

strlen是函数,计算的是字符串中字符数目,不包括'\0'

(5)strlwr和strupr函数——字符串大小写

strlwr是把大写字母换成小写字母

strupr是把小写字母换成大写字母

总结:C-风格字符串输入——cin或者getline或者get,cin是以空格或换行结束,getline和get都是读取一行。

getline函数会读取并丢弃换行符,而get函数如果带参会保留换行符在输入缓冲区,get函数如果不带参可以读取并丢弃换行符。

3,string类

 

(1)定义,初始化,输入输出

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str="abc";
    cin>>str;
    cout<<str;
    return 0;
}

(2)string的赋值、计算长度、翻转

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str1="abc";
    string str2=str1;
    reverse(str2.begin(),str2.end());
    cout<<str1<<" "<<str1.length()<<" "<<str2;
    return 0;
}

(3)从string中取字符

可以用数组形式,也可以用迭代器形式

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str="abcde";
    cout<<str[2]; 
    string::iterator it=str.begin();
    cout<<*it;
    it=str.end()-1;
    cout<<*it;
    return 0;
}

输出:

cae

(4)string的比较、连接

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str1="abc",str2="def",str3="def";
    if(str1<str2)cout<<"str1<str2\n";
    if(str2==str3)cout<<"str2=str3\n";
    cout<<str1+str2;
    return 0;
}

输出:

str1<str2
str2=str3
abcdef

(5)string转字符数组

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str="abc";
    const char *p1=str.c_str();
    char *p2=new char[20];
    strcpy(p2,p1);
    cout<<p2;
    return 0;
}

(6)string中查找字符

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string str="abca";
    cout<<str.find('a')<<" "<<str.find('a',2);
    return 0;
}

输出:

0 3

附上c++课本上的string常用函数:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值