我正在学习c++,我刚刚开始学习Qt的一些功能来编写GUI程序。我问了自己以下问题:

c++以前没有语法能够要求操作系统提供一个窗口或一种通过网络进行通信的方式(我承认我也不完全理解api),它是如何通过c++本身编写的库突然获得这些功能的?在我看来,这一切都是可怕的循环。你能在这些库中找到哪些c++指令?

我知道这个问题对一个有经验的软件开发人员来说似乎微不足道,但我已经研究了几个小时,没有找到任何直接的回答。这已经到了我无法理解Qt教程的地步,因为库的存在对我来说是不可理解的。


当前回答

你是对的,一般来说,图书馆不能使任何不可能的事情成为可能。

但是库并不一定要用c++来编写才能被c++程序使用。即使它们是用c++编写的,它们也可能在内部使用其他不是用c++编写的库。因此,c++没有提供任何方法来做这件事的事实并不妨碍它被添加,只要在c++之外有某种方法来做这件事。

在相当低的级别上,c++(或C)调用的一些函数将用汇编编写,并且该汇编包含所需的指令,以完成在c++中不可能(或不容易)的操作,例如调用一个系统函数。此时,该系统调用可以做计算机能够做的任何事情,因为没有任何东西可以阻止它。

其他回答

C和c++有两个属性,允许OP所谈论的所有这些可扩展性。

C和c++可以访问内存 C和c++可以调用非C或c++语言中的汇编代码。

在内核或基本的非保护模式平台中,外围设备(如串口或磁盘驱动器)以与RAM相同的方式映射到内存映射中。内存是一系列的开关,翻转外围设备的开关(比如串口或磁盘驱动器)可以让外围设备做一些有用的事情。

在保护模式操作系统中,当用户希望从用户空间访问内核时(比如写入文件系统或在屏幕上绘制像素),需要进行系统调用。C语言没有指令来进行系统调用,但是C语言可以调用汇编代码来触发正确的系统调用,这就是为什么C代码可以与内核对话。

为了使特定平台的编程更容易,系统调用被包装在更复杂的函数中,这些函数可以在自己的程序中执行一些有用的函数。可以直接调用系统调用(使用汇编程序),但是使用平台提供的一个包装器函数可能更容易。

还有另一层API比系统调用有用得多。以malloc为例。这不仅会调用系统来获取大块的内存,而且还会通过对正在发生的事情进行所有簿记来管理这些内存。

Win32 api用一个公共平台小部件集包装了一些图形功能。Qt更进一步,以跨平台的方式包装了Win32(或X Windows) API。

从根本上讲,C编译器将C代码转换为机器代码,由于计算机被设计为使用机器代码,所以你应该期望C能够完成计算机所能完成的大部分工作。包装器库所做的一切都是为您做繁重的工作,这样您就不必做了。

创建窗口不需要特殊的语法。所需要的只是操作系统提供一个API来创建窗口。这样的API由c++提供语法的简单函数调用组成。

此外,C和c++是所谓的系统编程语言,能够访问任意指针(可能由硬件映射到某些设备)。此外,调用在程序集中定义的函数也相当简单,这允许处理器提供的全部操作。因此,使用C或c++和少量的汇编程序来编写操作系统本身是可能的。

还应该提到Qt是一个不好的例子,因为它使用所谓的元编译器来扩展c++的语法。然而,这与它调用操作系统提供的api来实际绘制或创建窗口的能力无关。

语言(如c++ 11)是纸上的规范,通常用英语编写。看看最新的c++ 11草案(或者从ISO供应商那里购买昂贵的最终规范)。

你通常使用带有语言实现的计算机(原则上你可以在没有任何计算机的情况下运行c++程序,例如使用一群人类奴隶来解释它;那样既不道德又低效)

你的c++实现通常工作在某个操作系统之上,并与它通信(使用一些特定于实现的代码,通常在一些系统库中)。通常这种通信是通过系统调用完成的。在instance into syscalls(2)中查找Linux内核上可用的系统调用列表。

从应用程序的角度来看,系统调用是一个基本的机器指令,就像x86-64上的SYSENTER一样,具有一些约定(ABI)。

在我的Linux桌面上,Qt库位于X11客户端库之上,通过X Windows协议与X11服务器Xorg通信。

在Linux上,在可执行文件上使用ldd来查看对库的依赖项的(长)列表。在正在运行的进程上使用pmap查看在运行时“加载”了哪些文件。顺便说一句,在Linux上,你的应用程序可能只使用免费软件,你可以研究它的源代码(从Qt,到Xlib, libc,…内核)以了解更多正在发生的事情

首先,我觉得有一点误会

c++以前没有语法能够要求操作系统提供一个窗口或一种通过网络通信的方式,它是如何做到的呢

没有用于操作系统操作的语法。这是语义学的问题。

突然通过c++本身编写的库获得这些功能

操作系统主要是用c编写的。您可以使用共享库(即dll)来调用外部代码。此外,操作系统代码可以在系统调用*或中断上注册系统例程,您可以使用程序集调用这些例程。共享库通常只是为您进行系统调用,因此您可以避免使用内联汇编。

这里有一个很好的教程:http://www.win.tue.nl/~aeb/linux/lk/lk-4.html 它适用于Linux,但原理是相同的。

操作系统如何对显卡、网卡等进行操作?这是一个非常广泛的主题,但大多数情况下你需要访问中断,端口或写入一些数据到特殊的内存区域。由于这些操作是受保护的,所以无论如何都需要通过操作系统调用它们。

When you try to draw something on the screen, your code calls some other piece of code which calls some other code (etc.) until finally there is a "system call", which is a special instruction that the CPU can execute. These instructions can be either written in assembly or can be written in C++ if the compiler supports their "intrinsics" (which are functions that the compiler handles "specially" by converting them into special code that the CPU can understand). Their job is to tell the operating system to do something.

When a system call happens, a function gets called that calls another function (etc.) until finally the display driver is told to draw something on the screen. At that point, the display driver looks at a particular region in physical memory which is actually not memory, but rather an address range that can be written to as if it were memory. Instead, however, writing to that address range causes the graphics hardware to intercept the memory write, and draw something on the screen. Writing to this region of memory is something that could be coded in C++, since on the software side it's just a regular memory access. It's just that the hardware handles it differently. So that's a really basic explanation of how it can work.