灵感来自这个问题.
我们可以char用字符串文字初始化指针:
char *p = "ab";
Run Code Online (Sandbox Code Playgroud)
它完全没问题.人们可以认为它等同于以下内容:
char *p = {'a', 'b', '\0'};
Run Code Online (Sandbox Code Playgroud)
但显然事实并非如此.而且不仅因为字符串文字存储在只读内存中,而且即使通过字符串文字具有一种类型的char数组,并且初始化程序{...}具有char数组类型,两个声明的处理方式也不同,因为编译器是发出警告:
警告:标量初始化程序中的多余元素
在第二种情况下.这种行为的解释是什么?
更新:
此外,在后一种情况下,指针p将具有0x61(第一个数组元素的值'a')而不是存储器位置的值,使得编辑器在警告时仅采用初始化器的第一个元素并将其分配给p.
我是 C++17 和std::string_view. 我了解到它们不是空终止的,必须小心处理。
这是 printf() 的正确方法吗?
#include<string_view>
#include<cstdio>
int main()
{
std::string_view sv{"Hallo!"};
printf("=%*s=\n", static_cast<int>(sv.length()), sv.data());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
(或者将它与任何其他 printf 风格的函数一起使用?)
有两个相关的C标准规则:
C99标准,6.3.2.3:
指向void的指针可以转换为指向任何不完整或对象类型的指针.指向任何不完整或对象类型的指针可能会转换为指向void的指针并再次返回; 结果应该等于原始指针.
而且7.20.1.4:
以下类型指定一个无符号整数类型,其属性是任何有效的void指针都可以转换为此类型,然后转换回指向void的指针,结果将等于原始指针:
uintptr_t
这意味着,以下代码符合:
int *p = NULL;
void *q = (void*)p;
uintptr_t s = (uintptr_t)q;
Run Code Online (Sandbox Code Playgroud)
但它真的需要两步演员吗?如果执行以下操作,编译器是否会执行隐式中间转换:
int *p = NULL;
uintptr_t s = (uintptr_t)p;
Run Code Online (Sandbox Code Playgroud)
(好吧,它可能会出现在大多数编译器上,但我的问题是关于标准符合性)
我遇到了一段代码,进行了以下初始化:
static const uint8_t s[] = {"Some string"};
Run Code Online (Sandbox Code Playgroud)
我希望它解释如下:右侧是一个char指针数组与单个元素匹配,指向字符串文字"Some string".而左边的部分是一个数组uint8_t.然后我期望的行为是第一个s接收字符串文字指针的截断值的元素,因此在下面的代码中引起意外行为,假设s是一个字符串.
我做了以下测试代码:
#include <stdint.h>
#include <stdio.h>
static const uint8_t s1[] = "String1";
static const uint8_t s2[] = { "String2" };
int main(void){
printf("%p, %p\n", s1, s2);
printf("%s, %s\n", s1, s2);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,它似乎没有发生.不仅代码将正常工作,而且拆装表明,无论s1与s2被初始化为相同的方式对应的字符串.
这是gcc具体的吗?C语法是否允许将单个字符串文字带入{}并仍然将其解释为字符串文字?
使用硬件时,有时需要从特定寄存器执行读取,丢弃实际值(例如,清除某些标志).一种方法是明确读取和丢弃值,例如:
int temp = *(volatile int*)0x1234; // 0x1234 is the register address
(void)temp; // To silence the "unused" warning
Run Code Online (Sandbox Code Playgroud)
另一种似乎有效的方法是:
*(volatile int*)0x1234;
Run Code Online (Sandbox Code Playgroud)
但这似乎并没有明显暗示读访问,但它似乎转换为我检查的编译器上的一个.这是否由标准保证?
ARM GCC示例-O3:https:
//arm.godbolt.org/z/9Vmt6n
void test(void)
{
*(volatile int *)0x1234;
}
Run Code Online (Sandbox Code Playgroud)
翻译成
test():
mov r3, #4096
ldr r3, [r3, #564]
bx lr
Run Code Online (Sandbox Code Playgroud) 我正在从底部为Beagle Bone板构建一个Linux系统.我编译了vanilla内核并构建了一个基本的根文件系统busybox.系统使用U-boot启动,而rootfs它位于Linux PC上并通过NFS导出:
/path/to/rootfs 10.42.0.17(rw,wdelay,no_root_squash,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
Run Code Online (Sandbox Code Playgroud)
U-boot bootargs是:
bootargs console=ttyO0,115200n8 root=/dev/nfs rw nfsroot=${serverip}:/path/to/rootfs,v3,tcp ip=dhcp
Run Code Online (Sandbox Code Playgroud)
我在尝试su为非root用户工作时遇到了问题.为了解决这个问题,互联网上的人们建议设置二进制suid位busybox.这样做之后:
$ sudo chmod u+s busybox
Run Code Online (Sandbox Code Playgroud)
并验证:
$ ls -la
...
-rwsr-xr-x 1 myuser myuser 1882976 Jan 13 21:47 busybox
...
$ stat -c "%a %n" busybox
4755 busybox
Run Code Online (Sandbox Code Playgroud)
有些不对劲.内核正在启动并显示所有常用消息,但最后会卡住,并且不会login显示任何行.以下是启动顺序的最后几行:
[ 3.776185] IP-Config: Complete:
[ 3.779656] device=eth0, hwaddr=c8:a0:30:c5:80:e9, ipaddr=10.42.0.17, mask=255.255.255.0, gw=10.42.0.1
[ 3.789877] host=10.42.0.17, domain=, nis-domain=(none)
[ 3.795822] bootserver=10.42.0.1, rootserver=10.42.0.1, rootpath=
[ 3.802492] nameserver0=10.42.0.1 …Run Code Online (Sandbox Code Playgroud) 每个转换规范由字符%引入.在%之后,以下顺序出现:
- 零个或多个标志[...].
- 可选的最小字段宽度.[...]
- 一个可选的精度,它提供了为s转换写入的最大字节数.精度采用句点(.)后跟[...]可选十进制整数的形式;
- 可选的长度修改器[...].+转换说明符[...].
- 可选的最小字段宽度.[...]
- 转换说明符[...].
后来:
如果省略精度,则采用负精度参数.
printf("%.-1s\n", "foo")根据我如何解释标准定义,我期望得到什么:我从标准中得到的第二个引用表明我们可以传递一个负精度参数,并且这种精度会被忽略.
所以,printf("%.-1s\n", "foo")应该相当于printf("%s\n", "foo"),它会显示"foo\n"和返回4.
printf("%.-1s\n", "foo")我使用的系统(osx)上的实际行为:printf("%.-1s\n", "foo")显示" \n"和返回2.
这显然不同于我的预期.
我试图弄清楚 2d char 数组在内存中的样子。例如:
char c[][5]={"xa","ccc","bb","j","a","d"};
printf("TEST: %u %u %u %u \n\n",c[0],*c[0],c[0]+1,*(c[0]+1));
Run Code Online (Sandbox Code Playgroud)
输出:
测试:3214246874 120 3214246875 97
c[0]=*(c+0) 是字符串“xa”,等于 3214246874,所以我猜 c[0] 是字符数组“xa”的地址。当我将 * 放入 c[0] 时,我得到 120,即 ascii 中的“x”。
所以我认为 c 数组中的第一个空格是字符 x 的地址。之后,我用 c[0]+1 尝试了同样的方法,它打印了下一个地址,然后我输入了 *,我得到了 ,97,它是 ASCII 中的“a”。
所以我假设数组 c 看起来像这样:
c[0] c[1]
------------------------------------------------------------------
| pointer to x | pointer to a ||| pointer to c | pointer to c | etc ...
----------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
但是我在网上搜索并没有找到任何证据来证明我的假设。
在构建芯片 8 仿真器时,我遇到了芯片 8 信息的 2 个主要来源似乎不同的问题,这对整个芯片 8 解释器有影响。
一方面,我们有维基百科,它在操作码 FX65 下告诉我们
“用从地址 I 开始的内存中的值填充 V0 到 VX(包括 VX)。对于每个写入的值,I 增加 1。”
其中“对于写入的每个值,I 增加 1。” 是重要的部分。
遵循此结果会产生以下代码:
for(int i = 0; i <= ((opcode & 0x0F00) >> 8); ++i) {
V[i] = memory[I];
++I;
}
Run Code Online (Sandbox Code Playgroud)
另一方面,我们有cowgod的chip-8 参考,几乎每个教程都链接到的参考,它告诉我们以下内容
“解释器从位置 I 开始的内存读取值到寄存器 V0 到 Vx。”
应用此逻辑会产生以下代码(这也是大多数芯片 8 实现使用的实现):
for(int i = 0; i <= ((opcode & 0x0F00) >> 8); ++i) {
V[i] = memory[I …Run Code Online (Sandbox Code Playgroud)