由cout和printf来对函数传参过程的探讨

探讨了C和C++中由cout和printf对函数传参的过程,详细解析了在不同情况下变量和函数调用的顺序及结果,通过汇编代码展示了实际的参数传递过程。

由cout和printf来对函数传参过程的探讨

来看一个代码吧

#include<stdio.h>
int i=0;
int update(){i++;return i;}
int main()
{
	printf("update():%d i:%d\n",update(),i);
	return 0;
}

你觉得它的输出结果是什么
update():1 i:1 ?
,正确输出是update():1 i:0

为何会如此,先来做个实验康康吧:

#include<iostream>
#include <queue>
#include<stdio.h> 
using namespace std;
int i=0;
int update(){i++;return i;};
int main()
{
	printf("----------------------------test printf----------------------------\n");
	printf("before:%d\n",i);
	printf("update():%d i:%d\n",update(),i);
	printf("later:%d\n",i);
	
	printf("before:%d\n",i);
	printf("i:%d update():%d \n",i,update());
	printf("later:%d\n",i);
	
	printf("update1:%d update2:%d update3:%d",update(),update(),update());
}

它的输出结果是

----------------------------test printf----------------------------
before:0
update():1 i:0
later:1
before:1
i:2 update():2
later:2
update1:5 update2:4 update3:3

只有printf是这样的?不,可变参数的打印函数都具有这样的特性
我们来测试一下cout来验证一下

#include<iostream>
#include <queue>
#include<stdio.h> 
using namespace std;
int i=0;
int update(){i++;return i;};
int main()
{

	cout<<"---------------------------- test cout ----------------------------\n";
	i=0;
	cout<<"before:"<<i<<endl;
	cout<<"update():"<<update()<<" i:"<<i<<endl;
	cout<<"later:"<<i<<endl;
	
	cout<<"before:"<<i<<endl;
	cout<<"i:"<<i<<" update():"<<update()<<endl;
	cout<<"later:"<<i<<endl;
	
	cout<<"update1:"<<update()<<" update2:"<<update()<<" update3:"<<update()<<endl;

}

它的输出结果是:

---------------------------- test cout ----------------------------
before:0
update():1 i:0
later:1
before:1
i:2 update():2
later:2
update1:5 update2:4 update3:3

为什么会这样,这就必须说起c和c++中关于函数传参的过程了:

在汇编中函数传参要从最后一个参数到第一个参数分别入栈,这样函数取参数的时候,pop出来的顺序才是1-n。

因此,在printf("update():%d i:%d\n",update(),i);中,函数先压入i,再执行update(),将其返回值压入栈。

以上只是理论,真实的环境中由x86,x64,32位,16位机,它们的具体的传参方式略有不同:)

我们来看一下x64下这一条语句的汇编代码

mov     ebx, cs:i
call    _Z6updatev      ; update(void)
mov     r8d, ebx
mov     edx, eax
lea     rcx, aUpdateDID ; "update():%d i:%d\n"
call    _ZL6printfPKcz  ; printf(char const*,...)

在X64下,是寄存器传参. 前4个参数分别是 rcx rdx r8 r9进行传参.多余的通过栈传参.从右向左入栈。

上述代码中 格式化字符串在rcx中(最后一个赋值),update()返回值在edx,中第二个赋值。i在r8中第一个赋值 。

但是我们可以看到mov ebx, cs:i程序先将i的值移动到ebx中,再调用update函数,将返回值所在寄存器eax移动到edx中。

虽然过程有点变化,但是最后的结果还是和在汇编中函数传参要从最后一个参数到第一个参数分别入栈。

最后上一个题目吧//

#include <stdio.h>
int main()
{
    long long a = 1, b = 2, c = 3;
    printf("%d %d %d\n", a,b,c);
    return 0;
}

求输出

答案和解析

<think> 为了不使用标准库中的coutprintf,我们需要使用操作系统提供的底层接口来实现输出功能。在Windows系统中,可以使用WriteConsole函数;在Linux/Unix系统中,可以使用write系统调用。 以下是一个跨平台的简单实现思路: 1. 判断操作系统类型(Windows还是Linux/macOS)。 2. 根据不同的操作系统调用对应的底层API。 3. 实现一个简单的输出函数,支持字符串输出。 注意:这个实现仅用于演示目的,实际应用中还需要考虑更多细节(如错误处理、缓冲区管理等)。 下面是一个示例代码,实现了MyPrint函数,用于输出字符串: </think> ### 不使用 `cout` `printf` 实现输出功能 在 C++ 中,可以通过操作系统级别的系统调用直接实现输出功能。下面是两种跨平台的方法: #### 方法 1:使用 POSIX 的 `write` 系统调用 (Linux/macOS) ```cpp #include <unistd.h> // 包含 write() void my_print(const char* str) { // STDOUT_FILENO = 标准输出文件描述符 (值为1) write(STDOUT_FILENO, str, strlen(str)); } // 使用示例 int main() { my_print("Hello without cout/printf!\n"); } ``` #### 方法 2:使用 Windows API (Windows) ```cpp #include <windows.h> void my_print(const char* str) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); DWORD charsWritten; WriteConsole(hConsole, str, strlen(str), &charsWritten, NULL); } // 使用示例 int main() { my_print("Hello without cout/printf!\n"); } ``` #### 跨平台实现 ```cpp #include <cstring> #ifdef _WIN32 #include <windows.h> #else #include <unistd.h> #endif void platform_print(const char* str) { #ifdef _WIN32 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); DWORD charsWritten; WriteConsole(hConsole, str, static_cast<DWORD>(strlen(str)), &charsWritten, NULL); #else write(STDOUT_FILENO, str, strlen(str)); #endif } // 使用示例 int main() { platform_print("Cross-platform output without cout/printf!\n"); } ``` ### 实现原理说明 1. **操作系统级输出**: - 所有高级 I/O 函数最终都调用操作系统提供的系统调用 - Linux/macOS 使用 `write()` 系统调用(定义在 `<unistd.h>`) - Windows 使用 `WriteConsole()` API(需要 `<windows.h>`) 2. **文件描述符/句柄**: - Linux/macOS:`STDOUT_FILENO` (值为 1) 表示标准输出 - Windows:`STD_OUTPUT_HANDLE` 表示标准输出句柄 3. **执行流程**: ```mermaid graph LR A[程序] --> B[系统调用] B --> C[操作系统内核] C --> D[终端/控制台] ``` 4. **与 cout/printf 的区别**: - 无缓冲:直接写入,不经过标准库的缓冲区 - 无格式化:只能输出原始字符串,不支持 `%d`/`%f` 等格式化 - 无类型安全:需要手动处理类型转换 ### 扩展:添加基本格式化功能 ```cpp #ifdef _WIN32 // Windows 实现... #else // Linux/macOS 实现... #endif void print_int(int value) { char buffer[32]; snprintf(buffer, sizeof(buffer), "%d", value); // 使用snprintf格式化 platform_print(buffer); } int main() { platform_print("Number: "); print_int(42); platform_print("\n"); } ``` ### 注意事项 1. 这些方法直接调用系统 API,跳过 C/C++ 标准库 2. 对于格式化输出,仍需使用 `snprintf` 等函数(但严格来说这仍属于标准库) 3. 完全避免标准库需要自行实现数字转字符串等底层功能 4. 在嵌入式系统中,可直接操作硬件寄存器实现输出 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值