宏定义\字符串 多行书写时换行

http://hi.baidu.com/abcd1f2/item/4ca222508d898da2adc85751

宏定义\字符串 多行书写时换行

字符串常量定义时的换行问题    如果我们在一行代码的行尾放置一个反斜杠,c语言编译器会忽略行尾的换行符,而把下一行的内容也算作是本行的内容。这里反斜杠起到了续行的作用。
    构建较长的字符串是续行的常见用途, 还有一个作用是定义跨行的宏。
    如果我们不使用反斜杠,当我们试图初始化一个跨多行的字符串是,c语言编译器就会发出警告。如下面的语句所示:
char letters[] = {"abcdefghijklmnopqrstuvwxyz
  ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
      但是我们在行尾使用反斜杠, 那么就可以吧字符串常量跨行书写, 如下所示:
      char letters[] = {"abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ"};    从续行的开始输入字符串,可以避免在整个字符串中加入多于的空格。综上所述,上面的语句定义了一个字符数组letters,
并将其初始化为如下的初值:"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  
   c语言中还有一种拆分字符串的方法,那就是将其写个多个相邻的字符串。这些字符串之间用0个或者多个空白、制作符以及换行符隔开。c语言编译器会自动将这些字符串连接起来。因此,下面的表达式:"one"  "two" "three" 实际上相当于 "onetwothree".因此前面跨行的初始化语句也可以用下面的形式完成:
char letters[] = {"abcdefghijklmnopqrstuvwxyz"
                            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"}下面给出一个例子,下面的三条printf语句实际上都只接受了参数,printf("Programing in c is fun\n"); //Programing in c is funprintf("Programming in c" " is fun\n"); // Programing in c is funprintf("Programming" " in c" " is fun"\n);//Programing in c is fun////////////////////////////////////////////////////////////////////////////////////////////////////////////////////宏定义有无参数宏定义带参数宏定义两种。
  无参数的宏定义的一般形式为
           # define 标识符 字符序列

其中# define之后的标识符称为宏定义名(简称宏名),要求宏名与字符序列之间用空格符分隔。这种宏定义要求编译预处理程序将源程序中随后所有的定名的出现(注释与字符串常量中的除外)均用字符序列替换之。前面经常使用的定义符号常量是宏定义的最简单应用。如有:
            # define TRUE 1
            # define FALSE 0
则在定义它们的源程序文件中,凡定义之后出现的单词TRUE将用1替代之;出现单词FALSE将用0替代之。
       在宏定义的#之前可以有若干个空格、制表符,但不允许有其它字符。宏定义在源程序中单独另起一行,换行符是宏定义的结束标志。如果一个宏定义太长,一行不够时,可采用续行的方法。续行是在键人回车符之前先键入符号""。注意回车要紧接在符号""之后,中间不能插入其它符号。  、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 宏定义说明宏定义有无参数宏定义带参数宏定义两种。
  无参数的宏定义的一般形式为
           # define 标识符 字符序列

其中# define之后的标识符称为宏定义名(简称宏名),要求宏名与字符序列之间用空格符分隔。这种宏定义要求编译预处理程序将源程序中随后所有的定名的出现(注释与字符串常量中的除外)均用字符序列替换之。前面经常使用的定义符号常量是宏定义的最简单应用。如有:
            # define TRUE 1
            # define FALSE 0
则在定义它们的源程序文件中,凡定义之后出现的单词TRUE将用1替代之;出现单词FALSE将用0替代之。
       在宏定义的#之前可以有若干个空格、制表符,但不允许有其它字符。宏定义在源程序中单独另起一行,换行符是宏定义的结束标志。如果一个宏定义太长,一行不够时,可采用续行的方法。续行是在键人回车符之前先键入符号""。注意回车要紧接在符号""之后,中间不能插入其它符号。
      宏定义的有效范围称为宏定义名的辖域,辖域从宏定义的定义结束处开始到其所在的源程序文件末尾。宏定义名的辖域不受分程序结构的影响。可以用预处理命令#undef终止宏定义名的辖域。
  在新的宏定义中,可以使用前面已定义的宏名。例如,
             # define R 2.5
             # define PI 3.1415926
             # define Circle 2*PI*R
             # define Area PI* R * R
程序中的Circle被展开为2*3.1415926* 2.5, Area被展开为3.1415926*2.5*2.5。
     如有必要,宏名可被重复定义。被重复定义后,宏名原先的意义被新意义所代替。

     通常,无参数的宏定义多用于定义常量。程序中统一用宏名表示常量值,便于程序前后统一,不易出错,也便于修改,能提高程序的可读性和可移植性。特别是给数组元素个数一个宏定义,并用宏名定义数组元素个数能部分弥补数组元素个数固定的不足。
      注意:预处理程序在处理宏定义时,只作字符序列的替换工作,不作任何语法的检查。如果宏定义不当,错误要到预处理之后的编译阶段才能发现。宏定义以换行结束,不需要分号等符号作分隔符。如有以下定定义:
   # define PI 3.1415926;
原希望用PI求圆的周长的语句
   c=2*PI*r;
经宏展开后,变成
   c=2*3.1415926*r;
这就不能达到希望的要求。
  带参数宏定义进一步扩充了无参数宏定义的能力,在字符序列替换同时还能进行参数替换。带参数定定义的一般形式为
  # define 标识符(参数表)字符序列
其中参数表中的参数之间用逗号分隔,字符序列中应包含参数表中的参数。
在定义带参数的宏时,宏名标识符与左圆括号之间不允许有空白符,应紧接在一起,否则变成了无参数的宏定义。如有宏定义:
   # define MAX(A,B) ((A) > (B)?(A):(B))

则代码 y= MAX( p+q, u+v)将被替换成 y=((p+q) >(u+v)?(p+q):(u+v)。
           程序中的宏调用是这样被替换展开的,分别用宏调用中的实在参数字符序列(如p+q和u+V) 替换宏定义字符序列中对应所有出现的形式参数(如用p+q替代所有形式参数A,用u+V替代所有形式参数B),而宏定义字符序列中的不是形式参数的其它字符则保留。这样形成的字符序列,即为宏调用的展开替换结果。宏调用提供的实在参数个数必须与宏定义中的形式参数个数相同。
       注意:宏调用与函数调用的区别。函数调用在程序运行时实行,而宏展开是在编译的预处理阶段进行;函数调用占用程序运行时间,宏调用只占编译时间;函数调用对实参有类型要求,而宏调用实在参数与宏定义形式参数之间没有类型的概念,只有字符序列的对应关系。函数调用可返回一个值,宏调用获得希望的C代码。另外,函数调用时,实参表达式分别独立求值在前,执行函数体在后。宏调用是实在参数字符序列替换形式参数。替换后,实在参数字符序列就与相邻的字符自然连接,实在参数的独立性就不一定依旧存在。如下面的宏定义:
   # define SQR(x) x*x
希望实现表达式的平方计算。对于宏调用
   P=SQR(y)
能得到希望的宏展开p= y*y。但对于宏调用q=SQR(u+v)得到的宏展开是q=u+V*u+V。显然,后者的展开结果不是程序设计者所希望的。为能保持实在参数替换后的独立性,应在宏定义中给形式参数加上括号。进一步,为了保证宏调用的独立性,作为算式的宏定义也应加括
号。如 SQR宏定义改写成:
   # define SQR((x)*(x))
才是正确的宏定义。

      对于简短的表达式计算函数,或为了提高程序的执行效率、避免函数调用时的分配存储单元、保留现场、参数值传递、释放存储单元等工作。可将函数定义改写成宏定义。所以合理使用宏定义,可以使程序更简洁。


使用一些宏跟踪调试

A N S I标准说明了五个预定义的宏名。它们是:

_ L I N E _ (两个下划线),对应%d

_ F I L E _    对应%s

_ D A T E _   对应%s

_ T I M E _   对应%s

_ S T D C _

如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序

也许还提供其它预定义的宏名。

_ L I N E _及_ F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。

_ D AT E _宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。

源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。

如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是

非标准的。

可以定义宏,例如:

当定义了_DEBUG,输出数据信息和所在文件所在行

#ifdef _DEBUG

#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%s”,date,_LINE_,_FILE_)

#else

      #define DEBUGMSG(msg,date)

#endif

20,宏定义防止使用是错误

用小括号包含。

例如:#define ADD(a,b) (a+b)

用do{}while(0)语句包含多语句防止错误

例如:#difne DO(a,b) a+b;\

                   a++;

应用时:if(….)

          DO(a,b); //产生错误

        else

        

解决方法: #difne DO(a,b) do{a+b;\

                   a++;}while(0)


宏中"#"和"##"的用法
一、一般用法
我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起(这里说的是在预处理是对源文件的操作).
用法:
#include<cstdio>
#include<climits>
using namespace std;

#define STR(s)     #s
#define CONS(a,b) int(a##e##b)

int main()
{
printf(STR(vck));           // 输出字符串"vck"
printf("%d\n", CONS(2,3)); // 2e3 输出:2000
return 0;
}

二、当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用''#''或''##''的地方宏参数是不会再展开.

1, 非''#''和''##''的情况
#define TOW      (2)
#define MUL(a,b) (a*b)

printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2).

2, 当有''#''或''##''的时候
#define A          (2)
#define STR(s)     #s
#define CONS(a,b) int(a##e##b)

printf("int max: %s\n", STR(INT_MAX));    // INT_MAX #include<climits>
这行会被展开为:
printf("int max: %s\n", "INT_MAX");

printf("%s\n", CONS(A, A));               // compile error
这一行则是:
printf("%s\n", int(AeA));

INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏.
加这层宏的用意是把所有宏的参数在中间层里全部展开
, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.

#define A           (2)
#define _STR(s)     #s
#define STR(s)      _STR(s)          // 转换宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b)   _CONS(a,b)       // 转换宏

printf("int max: %s\n", STR(INT_MAX));          // INT_MAX,int型的最大值,为一个变量 #include<climits>
输出为: int max: 0x7fffffff
STR(INT_MAX) --> _STR(0x7fffffff) 然后再转换成字符串;

printf("%d\n", CONS(A, A));
输出为:200
CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))

三、''#''和''##''的一些应用特例
1、合并匿名变量名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号;
第一层:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二层:                        --> ___ANONYMOUS1(static int, _anonymous, 70);
第三层:                        --> static int _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;

2、填充结构
#define FILL(a)   {a, #a}

enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相当于:
MSG _msg[] = {{OPEN, "OPEN"},
{CLOSE, "CLOSE"}};

3、记录文件名
#define _GET_FILE_NAME(f)   #f
#define GET_FILE_NAME(f)    _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

4、得到一个数值类型所对应的字符串缓冲大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof "0x7fffffff"];
这里相当于:
char buf[11];


历史记录 avater ..\TCPServer\tcpserver.cpp(29): error C2001: 常量中有换行符 ..\TCPServer\tcpserver.cpp(30): error C2143: 语法错误: 缺少“)”(在“return”的前面) ..\TCPServer\tcpserver.cpp(30): error C2143: 语法错误: 缺少“;”(在“return”的前面) ..\TCPServer\tcpserver.cpp(86): error C2664: “QMutexLocker::QMutexLocker(QBasicMutex *)”: 无法将参数 1 从“const QMutex *”转换为“QBasicMutex *” ..\TCPServer\tcpserver.cpp(86): note: 转换丢失限定符 D:\tools\qt_14\5.14.2\msvc2017\include\QtCore\qmutex.h(226): note: 参见“QMutexLocker::QMutexLocker”的声明 ..\TCPServer\tcpserver.cpp(219): error C2001: 常量中有换行符 ..\TCPServer\tcpserver.cpp(220): error C2143: 语法错误: 缺少“;”(在“}”的前面) jom: D:\AiTest\build-TCPServer-Desktop_Qt_5_14_2_MSVC2017_32bit-Debug\Makefile.Debug [debug\tcpserver.obj] Error 2 jom: D:\AiTest\build-TCPServer-Desktop_Qt_5_14_2_MSVC2017_32bit-Debug\Makefile [debug] Error 2 09:34:00: 进程"D:\tools\qt_14\Tools\QtCreator\bin\jom.exe"退出,退出代码 2 。 Error while building/deploying project TCPServer (kit: Desktop Qt 5.14.2 MSVC2017 32bit) The kit Desktop Qt 5.14.2 MSVC2017 32bit has configuration issues which might be the root cause for this problem. When executing step "Make" 分析在qt 5.14.2中编译产生上述问题的原因,并给出修复此问题的方法及理由 回答 我们正在处理一个关于Qt 5.14.2编译TCPServer项目出现的多个错误的问题。根据引用内容,我们主要关注三个错误:C2001、C2143和C2664。我们将逐一分析这些错误的原因,并提供修复方法及理由。 1. error C2001: 常量中有换行符 原因分析: 这个错误通常出现在字符串常量中包含了换行符,或者字符串没有正确结束。在Qt中,这可能是由于字符串中包含了未转义的特殊字符(如换行符),或者字符串跨行没有使用正确的连接方式(例如,没有在行末使用反斜杠\字符串连接运算符)。另外,在Qt中,如果使用tr()函数进行多语言翻译字符串跨行也可能导致此问题。 修复方法: 确保字符串常量不包含未转义的换行符。如果需要多行字符串,可以使用以下方式之一: 使用反斜杠\在行末进行连接,例如: C++ const char *str = "This is a very long string that \ spans multiple lines"; 使用多个字符串并置(编译器会自动连接相邻的字符串),例如: C++ const char *str = "This is a very long string that " "spans multiple lines"; 检查字符串中是否包含未转义的特殊字符(如"、'、\n等),确保它们被正确转义(例如,\n表示换行\"表示双引号)。 在Qt的tr()函数中,如果字符串需要跨行,同样使用上述方法。 理由: C++标准要求字符串常量必须在同一行内,除非使用上述方法进行多行连接。否则,编译器会认为字符串被截断,从而报错。 2. error C2143: 语法错误: 缺少“;”(在“namespace”的前面) 原因分析: 这个错误通常表示在某个命名空间声明之前缺少分号。这可能是由于前一个语句(如类定义、结构体定义、变量声明等)没有以分号结束。例如: C++ class MyClass { // ... } // 这里缺少分号 namespace MyNamespace { // ... } 在上面的代码中,类定义结束后缺少分号,导致编译器在遇到namespace关键字报错。 修复方法: 检查命名空间声明之前的代码,特别是类、结构体、枚举等定义,确保它们以分号结束。 检查头文件中的宏定义,确保没有错误地截断了语句。 理由: C++语法要求类、结构体等定义结束后必须加上分号,否则编译器会认为后续的代码(如命名空间)是当前定义的一部分,从而产生语法错误。 3. error C2664: 无法将参数从“const char [n]”转换为“LPCWSTR” 原因分析: 这个错误通常发生在Windows编程中,当函数期望一个宽字符字符串(LPCWSTR,即const wchar_t*)但传入的是窄字符字符串(const char*)。在Qt项目中,这可能是因为项目设置中使用了Unicode字符集(默认),而代码中使用了窄字符串。 修复方法: 有以下几种解决方案: 将字符串转换为宽字符串:使用L前缀将字符串字面量转换为宽字符串,例如: C++ functionExpectingLPCWSTR(L"wide string"); 使用QString的转换函数:如果使用Qt,可以利用QString来转换: C++ functionExpectingLPCWSTR(QString("narrow string").utf16()); 或者,如果函数接受LPCWSTR,可以直接使用QString::toStdWString(): C++ std::wstring str = QString("narrow string").toStdWString(); functionExpectingLPCWSTR(str.c_str()); 更改项目字符集设置(不推荐,因为可能会影响整个项目的一致性): 在项目属性中,将“字符集”从“使用Unicode字符集”改为“使用多字节字符集”。具体步骤: 右键单击项目 -> 属性 -> 配置属性 -> 常规 -> 字符集 -> 改为“使用多字节字符集”。 理由: Windows API有两种版本:接受char*的ANSI版本和接受wchar_t*的Unicode版本。在Visual Studio中,默认使用Unicode字符集,因此许多API会映射到Unicode版本(期望LPCWSTR)。如果传入窄字符串,就会导致类型不匹配。 针对Qt项目的额外建议 在Qt项目中,我们通常使用QString来处理字符串,这样可以避免很多字符集问题。当需要调用Windows API,可以使用QString的转换函数(如utf16())来获得宽字符串。 另外,注意Qt的tr()函数用于多语言翻译,它返回的是QString,因此可以直接用于需要宽字符串的场合(通过utf16()转换)。 总结 在编译TCPServer项目,遇到这些错误可以按照以下步骤修复: C2001:检查字符串常量,确保跨行正确连接,并转义特殊字符。 C2143:检查命名空间之前的代码,确保类、结构体等定义以分号结束。 C2664:根据情况将窄字符串转换为宽字符串,或者调整项目字符集设置(但推荐使用宽字符串转换,以保持Unicode支持)。 相关问题 在Qt中如何正确处理多语言字符串以避免C2001错误? 在Qt项目中,如何统一处理Windows API的字符串参数以避免C2664错误? 如何配置Qt项目以同支持Unicode和多字节字符集? Qt 5.14.2编译TCPServer项目错误分析与修复 1. error C2001: 常量中有换行符 原因: 字符串常量包含未转义的换行符或跨行书写未正确连接。常见于中文字符串或跨行文本未使用\连接符或相邻字符串自动连接规则。 修复方法: C++ // 错误示例 QString str = "第一行 第二行"; // 正确示例(使用转义) QString str = "第一行\n第二行"; // 或使用相邻字符串自动连接 QString str = "第一行" "第二行"; 理由: C++要求字符串常量必须在单行内或显式连接,跨行书写会导致编译器识别为未结束的字符串3。 2. error C2143: 缺少’;'(在’namespace’前面) 原因: 通常由头文件缺失分号引起,尤其在类/结构体定义后漏写;。例如: C++ class MyClass {} // 缺少分号 namespace MyNamespace { ... } 修复方法: 检查所有类/结构体定义结尾是否补充分号: C++ class MyClass {}; // 补充分号 理由: C++语法要求类型定义后必须用;表示结束,否则编译器会将后续namespace识别为语法错误2。 3. error C2664: 无法将参数从"X"转换为"Y" 原因: Qt项目默认启用Unicode字符集,但代码使用了窄字符串(char*),而API需要宽字符串(wchar_t*)。常见于Windows API调用或Qt跨版本兼容问题。 修复方法: 方案1:使用QString转换 C++ // 错误 ::CreateFile("file.txt", ...); // 正确 ::CreateFile(QString("file.txt").toStdWString().c_str(), ...); 方案2:禁用Unicode模式(不推荐) 项目属性 → C/C++ → 语言 → 符合模式 → 设为否1 方案3:显式指定宽字符 C++ ::CreateFile(L"file.txt", ...); // 添加L前缀 理由: Qt 5默认使用UTF-16编码,窄字符串与宽字符串类型不匹配导致转换失败13。 关键修复建议 字符串处理 所有中文字符串使用QString::fromUtf8("中文") 跨行字符串\转义或分多段书写 头文件检查 确保所有类/结构体定义以;结尾 检查头文件嵌套顺序(Qt头文件放在标准库前) 字符集统一 在pro文件添加: qmake win32: QMAKE_CXXFLAGS += /execution-charset:utf-8 /source-charset:utf-8 避免直接使用char*,优先用QString 信号槽参数 检查信号槽签名是否一致,使用qRegisterMetaType()注册自定义类型。 通过上述修正可解决90%的Qt编译错误,核心原则:严格处理字符串边界、显式类型转换、检查语法终结符。 相关问题 如何在Qt中彻底解决中文字符串导致的编译错误? Qt项目从VS迁移到MinGW出现类似错误该如何处理? 为什么Qt 5默认使用Unicode字符集?这带来了哪些优势和挑战? 思维导图 参考来源 优快云 优快云 优快云 使用AI进行编程,合适的提问方式流程是什么样
最新发布
08-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值