我正在学习c++,我刚刚开始学习Qt的一些功能来编写GUI程序。我问了自己以下问题:
c++以前没有语法能够要求操作系统提供一个窗口或一种通过网络进行通信的方式(我承认我也不完全理解api),它是如何通过c++本身编写的库突然获得这些功能的?在我看来,这一切都是可怕的循环。你能在这些库中找到哪些c++指令?
我知道这个问题对一个有经验的软件开发人员来说似乎微不足道,但我已经研究了几个小时,没有找到任何直接的回答。这已经到了我无法理解Qt教程的地步,因为库的存在对我来说是不可理解的。
我正在学习c++,我刚刚开始学习Qt的一些功能来编写GUI程序。我问了自己以下问题:
c++以前没有语法能够要求操作系统提供一个窗口或一种通过网络进行通信的方式(我承认我也不完全理解api),它是如何通过c++本身编写的库突然获得这些功能的?在我看来,这一切都是可怕的循环。你能在这些库中找到哪些c++指令?
我知道这个问题对一个有经验的软件开发人员来说似乎微不足道,但我已经研究了几个小时,没有找到任何直接的回答。这已经到了我无法理解Qt教程的地步,因为库的存在对我来说是不可理解的。
当前回答
计算机就像一个洋葱,它有很多很多层,从纯硬件的内核到最外层的应用层。每一层都将自己的一部分暴露给下一个外层,这样外层就可以使用内层的一些功能。
以Windows为例,操作系统为运行在Windows上的应用程序公开了所谓的WIN32 API。Qt库使用该API为使用Qt的应用程序提供自己的API。你使用Qt, Qt使用WIN32, WIN32使用较低级别的Windows操作系统,等等,直到它成为硬件中的电信号。
其他回答
我如何看待这个问题,这实际上是一个编译器的问题。
这样看,你用汇编写了一段代码(你可以用任何语言写),它把你新写的语言(你想把z++称为汇编)翻译成汇编,为了简单起见,让我们称它为编译器(它是编译器)。
现在你给了这个编译器一些基本的函数,这样你就可以写int,字符串,数组等等,实际上你给了它足够的能力,所以你可以用z++来写编译器本身。现在你有了一个用z++编写的z++编译器,非常简洁。
更酷的是,现在您可以使用编译器已有的功能为其添加功能,从而通过使用以前的功能扩展z++语言的新功能
举个例子,如果你写了足够多的代码来绘制任何颜色的像素,那么你可以使用z++扩展它来绘制任何你想要的东西。
语言(如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++或任何允许直接访问该内存的语言写入该内存。该内存恰好可以被显卡访问或位于显卡上。
在现代系统中,由于各种限制,直接访问图形存储器需要编写驱动程序,因此您可以使用间接方法。库创建一个窗口(实际上只是一个像其他图像一样的图像),然后将该图像写入图形内存,然后GPU将其显示在屏幕上。除了写入特定内存位置的能力之外,该语言不需要添加任何东西,这就是指针的作用。
为了提供与其他答案略有不同的观点,我将这样回答。
(免责声明:我只是稍微简化了事情,我给出的情况纯粹是假设的,是作为演示概念的一种手段而写的,而不是100%真实的生活)。
从另一个角度考虑问题,假设您刚刚编写了一个简单的操作系统,具有基本的线程、窗口和内存管理功能。你想要实现一个c++库,让用户用c++编程,做一些事情,比如创建窗口,在窗口上绘图等等。问题是,如何做到这一点。
首先,由于c++编译为机器代码,您需要定义一种使用机器代码与c++进行接口的方法。这就是函数的作用所在,函数接受参数并给出返回值,因此它们提供了在不同代码段之间传输数据的标准方式。他们通过建立一种称为调用约定的东西来做到这一点。
A calling convention states where and how arguments should be placed in memory so that a function can find them when it gets executed. When a function gets called, the calling function places the arguments in memory and then asks the CPU to jump over to the other function, where it does what it does before jumping back to where it was called from. This means that the code being called can be absolutely anything and it will not change how the function is called. In this case however, the code behind the function would be relevant to the operating system and would operate on the operating system's internal state.
所以,几个月后,你已经整理好了所有的操作系统功能。你的用户可以调用函数来创建窗口并在上面绘图,他们可以创建线程和所有美妙的东西。但问题是,你的操作系统的功能将不同于Linux或Windows的功能。所以你决定给用户一个标准的界面,这样他们就可以编写可移植的代码。这就是QT的用武之地。
As you almost certainly know, QT has loads of useful classes and functions for doing the sorts of things that operating systems do, but in a way that appears independent of the underlying operating system. The way this works is that QT provides classes and functions that are uniform in the way they appear to the user, but the code behind the functions is different for each operating system. For example QT's QApplication::closeAllWindows() would actually be calling each operating system's specialised window closing function depending on the version used. In Windows it would most likely call CloseWindow(hwnd) whereas on an os using the X Window System, it would potentially call XDestroyWindow(display,window).
很明显,操作系统有很多层,所有这些层都必须通过各种各样的接口进行交互。有许多方面我还没有谈到,但要全部解释它们将需要很长时间。如果您对操作系统的内部工作原理有进一步的兴趣,我建议您查看OS dev wiki。
请记住,许多操作系统选择向C/ c++公开接口的原因是它们可以编译为机器代码,它们允许将汇编指令与它们自己的代码混合在一起,并且它们为程序员提供了很大程度的自由。
同样,这里有很多内容。我想继续解释库如何像.so和.dll文件不需要用C/ c++编写,可以用汇编或其他语言编写,但我觉得如果我再添加更多,我还不如写一整篇文章,尽管我很想这样做,但我没有一个站点来托管它。
好问题。每一个新的C或c++开发人员都有这样的想法。在本文的其余部分中,我假设使用的是标准的x86机器。如果你使用的是微软c++编译器,打开你的记事本并输入这个(命名为Test.c)
int main(int argc, char **argv)
{
return 0
}
现在编译这个文件(使用开发人员命令提示符)cl Test.c /FaTest.asm
现在打开Test。在你的记事本里。你看到的是翻译后的代码——C/ c++被翻译成汇编程序。你明白我的意思了吗?
_main PROC
push ebp
mov ebp, esp
xor eax, eax
pop ebp
ret 0
_main ENDP
C/ c++程序是设计来在金属上运行的。这意味着他们可以访问较低级别的硬件,从而更容易利用硬件的功能。比如说,我要在x86机器上编写一个C库getch()。
根据汇编器的不同,我可以这样输入:
_getch proc
xor AH, AH
int 16h
;AL contains the keycode (AX is already there - so just return)
ret
我用汇编程序运行它并生成一个。obj -命名为getch.obj。
然后我写了一个C程序(我不包括任何东西)
extern char getch();
void main(int, char **)
{
getch();
}
现在将这个文件命名为GetChTest.c。通过传递getch来编译这个文件。obj。(或者单独编译到.obj和LINK GetChTest。Obj和getch。Obj一起生成GetChTest.exe)。
运行GetChTest.exe,你会发现它在等待键盘输入。
C/C++ programming is not just about language. To be a good C/C++ programmer you need to have a good understanding on the type of machine that it runs. You will need to know how the memory management is handled, how the registers are structured, etc., You may not need all these information for regular programming - but they would help you immensely. Apart from the basic hardware knowledge, it certainly helps if you understand how the compiler works (ie., how it translates) - which could enable you to tweak your code as necessary. It is an interesting package!
这两种语言都支持__asm关键字,这意味着你也可以混合汇编语言代码。学习C和c++会让你成为一个更全面的程序员。
不需要总是与汇编程序链接。我提到它是因为我认为这会帮助你们更好地理解。大多数这样的库调用都利用了操作系统提供的系统调用/ api(操作系统负责硬件交互)。