loading...
Cover image for Is C still a high level language?

Is C still a high level language?

delta456 profile image Swastik Baranwal ・1 min read

I keep hearing that C is no longer a High Level Programming Language because it is no abstractions, has pointers, have to handle memory on our own, no string data type, unsafe etc.

Do you think that C is not a high level language anymore? I am against this because it's still readable as any other high level and usable as ever and has many libraries and still been updated.

Discussion

markdown guide
 

Depends on from which direction you are looking.

If you are looking from above (Javascript, Java, Python, etc.), it's low level.

If you are looking from below (machine code, assembly, etc.), it's high level.

itDepends

 

So it's mid level or old high level.

 
 

Ben,

I agree with your point of view - machine code and assembly are definitely lower level than C while Java, Javascript, Perl, and Python are higher level. I will say Pascal is at the same level as C.

-- Olu Adegoke

 

I agree, Pascal (and probably Fortran) are on the same level as C.

 

Well, when you have a CPU, the lowest programming language that most of us, mere mortals, can write for the CPU is assembler.

What C compiler does is none other than translating C syntax to assembler. It is still so low that In fact you can insert assembler snippets in C source code. There’s quite a lot of assembler code snippets in Linux kernel C source code.

So yeah, I think it’s pretty low level language because with C you can do almost everything that assembler can do and assembler is the lowest language you can write in.

 

Yeah, that takes me back. That's real programming. Not like the namby-pamby memory managed languages I use these days.

 

Thanks very much for that link. A copy of same was posted up in the common-room when I was at Uni, and was in my thoughts when I posted my comment, which was, of course, tongue-very-firmly-in-cheek.

 

If this is sarcasm, it's a little too good 😂

 

That it's such a boomer thing to say. Both low and high level programming languages serve their purpose. It's true memory managed languages can be quite slow in execution time and can be resource hungry. On the other hand, you can use C# for example for almost everything and it's suitable for advanced design patterns that are just impossible to implement on C. Unless of course you write your own abstraction level, in which case congrats you just re created C++. Furthermore, C# can be optimized to run almost as fast as C and all executables can be stripped down to run even faster. For Gods sake even python can be optimized to run relatively fast. It all depends on the requirements and the context of the application.

For Gods sake even python can be optimized to run relatively fast.

And you can always write C modules that you then import into Python.

 

This is really not true at all.

A modern C compiler does quite a lot of interesting work to try to make C code competitive.

Things are reordered and elided and optimized in ways that are often surprising.

Consider what this does in C.

while (1);

If you think it requires the program to loop forever, you are mistaken. :)

Beyond that, there is a great deal that C cannot do which assembly can.

  1. C has no concept of registers (the register storage class just makes a variable unaddressable).

  2. C has no concept of op codes.

  3. C has no concept of the CPUs memory model -- C has a segmented memory model.

C programs run in the C Abstract Machine, and understanding this is essential to writing C programs that do not work only by accident.

Consider the following snippet -- what do you expect it to do in C?

{
  int i = 1;
  printf("%d, %d\n", i++, i);
}
 

I agree with you for the most part, but that last paragraph is baseless.

I can insert assembler to Common Lisp and Rust. Both of those out match Python, Javascript, or C++ in abstraction power, so by definition, they are high languages. That doesn't mean that they can't reach down to lower levels of abstraction when they need to do so.

 
 

What does an assembly require?

  1. Access to the CPU registers? C doesn't have this.
  2. Access to the CPU's memory model? C doesn't have this.
  3. Access to the CPU's op codes? C doesn't have this.
  4. A trivial translation to machine code? C doesn't have this.

So, what do you mean by "assembly"?

And how does C qualify as one?

 
  1. Different CPUs have different registry set, so if you want portability it goes out of window.
  2. Not every CPU has a memory model, so if you want portability it goes out of window.
  3. Compiler translates program to CPU opcodes, so C does have that. You do not control which exact opcodes, but if you want portability it goes out of window.
  4. Could you show me an example of such nontrivial translation in C? i++, a[i], b.c, for, etc all are trivial (in context of particular CPU).

Let's look at Wikipedia, for what an "assembly" is to most people.

In computer programming, assembly language (or assembler language), is any low-level programming language in which there is a very strong correspondence between the instructions in the language and the architecture's machine code instructions.

"very strong correspondence between the instructions in the language and machine code instructions" nails it. I believe C has exactly that. For the sake of portability, it does not allow control of every little detail, as it builds on the lowest common denominator.

Every CPU has a memory model, but other than that you've done a good job of demonstrating why C isn't a portable assembly. :)

Beyond that, if you believe that C has a very strong correspondence to every machine code instruction set, then you believe that every machine code instruction set has a strong correspondence with every other machine code instruction set.

This is simply not true -- let's consider a forth machine.

Or if that's not enough, consider one which runs befunge in hardware.

Now let's consider your example operations.
What do you believe this is required to translate to?

void foo() {
  int i = 0;
  int a[3] = { 1, 2, 3 };
  struct foo { int c; } b;
  b.c = a[i++];
}

When you've done that, let me know what you think this does:

void bar() {
  while (1);
}

And then let's finish up with a more interesting example.
What does calling this do?

void quux() {
  int i = 1;
  printf("%d, %d\n", i++, i);
}

Oh yeah. Let's bring up examples out of domain of conversation, just to derail it. This is plain wasting of everyone time, just to pump up ego.
I do not take part in such "discussions". Bye.

I do get what youre saying, and tbh I didn't realize people were actually get egotistical or at all heated in this.

I don't think assembly is the right word for it, thats all.
It is portable, and does allow for many features that map easily to assembly,
even in some cases allowing for the register keyword etc (although it isnt always guaranteed)

I just think portable and assembly are opposites as assembly programming is very much about familiarity with hardware and programming a specific machine.

Thomaz,

"Could you show me an example of such nontrivial translation in C? i++, a[i], b.c, for, etc all are trivial (in context of particular CPU)."

Don't you remember asking for those examples? :)

 

It requires to much abstraction to truly be a "portable assembly"
I think the best example of a "portable assembly" would be something like LLVM IR

 

While that might be true, today, when it was made, C was meant to be a portable language. At some point, they stopped adding things that pretty much all hardware supports now (like SIMD), and they never did specify type sizes and structure packing rules. That was done in an effort to be either be more portable or more efficient, but not specifing things is what causes portability issues. Eventually, we got stdint.h, but packing rules are non-standard #pragmas, now.

C is very portable, and I'm not argueing with that, I'm agreeing.
I'm saying that it has an abstract machine that goes over everything, which is the only way to make code portable from one arch to another.
Things like the IR of compilier (LLVM was just the first thing to come to mind) are by definition portable since they can be sent through different backends, and they are much more low level, and more deserving of the title "portable assembly"

 

What exact abstraction do you mean?

You know you can have a C compiler in ~500 lines of C code?

You do have abstraction, in that you have types, structs, etc, which no actual assembly would have. Variables of different sizes etc.

These data types is just a way of thinking about chunks of memory. It is nice to have direct support in the language, but we did have these in assemblers already:

Even some higher level languages build these features in similar way:

That is true, but these aren't exactly standardized, and I usually think of them as assembler features as opposed to a "language feature"
I wouldn't really count something as being a part of assembly if it wasn't something directly linked to the actual nmeonics

 

To be frank, I think C should not be considered a high-level language anymore and using it for anything besides kernels, interpreters, and like is almost always a mistake.

Have you read Paul Graham's essay Beating the Averages? It's not explicitly about C, but he makes the general point that high-level languages vary in power (heading "The Blub Paradox"), and I think it applies even more strongly to C.

I find it strange that you consider C "as readable as any other high level language". Compare these code snippets:

for (int i = 0; i < argc; i++) printf("%s\n", argv[i]);
for arg in sys.argv: print(arg)

Are these really equally readable to you? This pattern will appear in almost any comparison of C code with equivalent code in a higher-level language.

And to say nothing of the difficulty of debugging and memory safety issues, which seem to behind a huge proportion of security vulnerabilities in C software.

 

C was made earlier than Python so they never thought of using this syntax.

 

Python engine is written in C

 

Well, other languages from those days already had the for(each) syntax. Source: quora.com/What-was-the-first-langu...

 

Well you can use puts instead of printf, but I take your point. I think readability in C is bad because of type unsafety, no defer statement and void pointers everywhere.

 

You might be interested in 3GL and 4GL definitions. However it's somewhat outdated.

 

When you want to work with objects, especially large objects, you often want to avoid moving the objects from one memory location to another as much as possible. This is where C and Cpp dominate, this is on the main reasons why significantly powerful applications can be run on relatively tiny hardware. This is especially true in video and vision applications, where one can, if skilled, map a network input buffer directly to GPU memory, skipping several memory moves altogether that a more abstract approach would require. In addition, highly performant apps often need to explicitly control when memory gets freed in order to avoid getting zapped by a gc getting busy at a random time. In addition, highly performant apps often reuse memory locations repeatedly rather than allocating and deallocating repeatedly. For reasons such as this, C and C++ are for many the highest level of abstraction which apply to the task at hand.

 

Technically going by the definition, C could be considered high level. I don't really see how it makes a difference, at the end of the day C nowadays is only employed in niche use cases like embedded systems, kernels, drivers, etc. The reason why it is avoided is because it doesn't have many abstractions, i.e. it is considered too low level for general programming.

 

I have some thoughts, so here we go:

  • First of all you need to know that concepts like "high-level language" are defined in the CS field. Read this article and consider checking out the CS course from Stanford or at Coursera. Concepts like that does not "change"
  • "It has no abstractions" you are wrong here. It has abstractions for basic types, concepts like Arrays and even syscalls. It also has abstractions for FILEs and a lot of things. If possible, check some basic C course and you will be amazed of how many things C can offer you!
  • "it has no pointers" you are also wrong. C has pointers, and pointers of pointers. They are representations of memory references.
  • "No string datatype": Do you ever wondered why the Strings and Arrays has so many methods in common? They are both almost the same thing: Arrays! 😁 Strings = Arrays of chars!
  • "C is still updated": When Linus was asked about changing from C to C++ the main language from Linux kernel, he answered with a message that C is a Stable language and C++ is continuously "messy" updated (with a lot of not so receptive words...)

C is a language to be used in some specific contexts. As we never should be doing a web-Scrapper using C when we have Python, I think we never will see an OS written in JS.

The answer to your question is: C is a high-level language and this will not change for the CS world too soon.

 

I never said that C has no pointers. I meant C++ std::string.

I keep hearing that C is no longer a High Level Programming Language because it is no abstractions, has pointers, have to handle memory on our own, no string data type, unsafe etc.
 

Actually, the tweet shared by this article said that, as you can see at this link:
twitter.com/ThePracticalDev/status...

About the STD: the C++ Standard Library is a implementation of common useful objects as abstractions. The existence of such thing in C++ doesn't invalidate that C has its own abstractions as I mentioned. I really suggest you to check how C language is constructed and how to implement some basic things such as a basic server with threads, open/read/write files or a basic calculator in C.

I have no intention to offend you but seems like you have no basic notion of how C language works or what is concepts like abstractions and basic data types.

I get what you mean but people complain that they have to use char chr[] or char* chr for this.

There's a big difference between people complaining about something and people being really mindful about knowing the options to it. Compare:

// C
char * str = "hello";
// C++
std::string str = "hello";

There are more letters to be written in C++ and you are including in your executable the full string.h header. Is that valid? Are you really using all of it? For a Desktop application, this makes no sense but for embedded systems, every byte counts.

  • "You can write using namespace std; and...": No. Do not do this in your code. Check this thread at StackOverflow and do some research about it and why is considered a bad practice.

For the 'string' issue, let me suggest another approach:

// C
typedef char * string;

string str = "hello";

As an answer to people who say things like: "C is bad because it doesn't have strings!", consider asking about what they are trying to do. In general, they are complaining about scapegoats.

Definitely! I really agree with you. I hope Dev deletes/changes their tweet.

 

There is no such thing as objectively high-level or low-level. Every language sits uppon a spectrum of abstraction levels. You could even say that CISC assemblies are higher level than RISC assemblies. C is the language that sits just over assemblies and intermediate representations (IRs) and below other compiled languages, like C#, Java, Go, JS, Python, etc. To me, it's supposed to be the language you'd use when another compiled language would create performance issues, but the project is too big to just write it all in assembly.

 

It's relative to what you're comparing to. I'd say it's lower level than some other languages, which is not a bad thing.

Arguably, one can consider most high level languages like Ruby, Python, Java, etc to be not that high level either because you still need to actually write instructions instead of just something more declarative and leaving the implementation up to the language.

 

Well, I will create another discussion here and ask if this classification "high level" x "low level" languages still does make sense. Maybe when you didn't have another abstraction layers it made sense to classify languages that way, but now i think we should have other classes like "systems language", "scripting language" or something like that. Low level and high level are too widen concepts nowadays

 

C was designed as a low-level language with high-level constructs. Brian Kernighan and Dennis Richie needed a language to write Unix in. BCPL, B, and rational fortran - ratfor all contributed to the language now known as C. C is meant to be unsafe and have flexible rules. It was so by design.

It was one of the early universal assembly languages.

 

Everyone should knew this was going to happen as every 5 years we see new languages include in their main features: "ease of use", "garbage collected", "transparent to dev memory allocation".
As a result, C is pushed out from the "high level lang." definition every time a language is born in popularity with those characteristics, and that's because today is so far away from those new "high level languages" we cannot consider it a high level lang anymore.

 

I think the term high-level language was only created to contrast them to low-level languages.

Low-level languages are languages that can be used for both low- and high-level programming.

High-level languages can only be used for high-level programming.

Think of writing a driver. You can probably only write it in low-level language. However, it doesn't mean high-level programs can't be written in it. So, C can be used for both, even though it's not a high-level language. That's only my take on this, I'm not an authority on this.

 

As a generic syntax and semantic that can express any behavior desired succinctly to be translated into any assembly, be it x86, ARM or you name it, yes. I would consider "high-level language" to be analogous to "can be made to compile into any instruction set".

 

Depends on who you ask. Some people who only know languages like ruby or javascript could look at some C code and maybe think "I can do all of that in one line of [prefered language]". Maybe that's the reason why they think C is no longer high level.

 

This is a tricky thing if I may say so. Even Go for example has pointers etc and needs some memory management handling. Some people call it a high level language and others mid level. Never ending arguments to follow

 

Great work I'm sriram hegde fullstack javascript developer I have a whatsapp group dedicated to coders so that you can chat and collaborate on fun hobby projects with real people if you are interested please ping me at +918970787208

 

Being high level or low level has nothing to do with it's readability. Higher level languages usually have GC and memory management, whereas low level languages do not.

 

I guess those who think that C is a low level language never attempted to write anything more than a helloworld in assembly. Granted modern languages have higher level abstractions than than C but the difference between ASM and C is still vastly bigger than between C and anything more modern.

 

Next question, "Did Adam have a belly button?"

To me this is along those lines.
The discussion leads no where.
And reality doesn't change one bit.
Choose the language you need/want to do what you need/want.

Someone below was commenting on how C# could be as/almost as fast as C. So what.

There's defintely one thing C# can do that C can't do...

Be thrown in the trash and no longer supported by Microsoft.

If one day MS decides the .NET Framework is "old news" AND they come out with a TNG Programming Structure using a new SeaStar language... they can end-of-life C#.
(Been there done that!)

C on the other hand will likely endure, due to its place and popularity in the history of programming languages.

 

It's becoming more of a high level language as time moves on and hardware evolves in ways C programming model doesn't account for (GPUs etc), while compilers evolve to take better advantage of C's abstractions (e.g. compilers remove calls to memset because they are "useless" under C model even if the programmer thought they were wiping out a password from memory)

 

C is the greatest and most powerful high-level assembly language ever. It rocks when you use it for low-level stuff--no better tool for what it does. I had the pleasure of writing compilers, operating systems and system-level code in it for 25 years. Sadly (in a way), I am condemned to write in Java the last 15-20 years of my career; it's just a fact of life today to remain employed.

 

It is a high level language, even the creators refered to it as such and nothing will change here. Basically everything that is not assembly is heigh level.

 

C is a language that is unique because it can provide both high and low level programming depending on the route you take. As mentioned by @benwtrent it really depends on how you look at it. Many languages are built on C because it's very easy to use but it's one of few languages that can connect and work with a computer down to the core level, which is perfect for memory management, working with ASM and of course building languages. This generally leads to it being low-level, not to mention that writing a language in C can be fairly simple once you grasp the basics of language development.

However from a high-level perspective take a look at what C can do, it has the capability to work alongside ASM, Machine Code, directly manipulate and handle memory addresses which is why it's very popular and why I fell in love with C.

 

From the assembler's perspective, C is high level. From the perspective of Java, C#, JavaScript etc. C is low-level. Does it really matter? I don't think so. What really matters imho is that no other language has managed to dethrone the inherently unsafe shoot-yourself-in-the-foot monstrosity of a language we call C in the areas it is used in. Go attempted it but failed. So did rust. And Elixir. And probably countless others. C should have died and should have been replaced with a better, safer language 10 years ago. But somehow, some way, it's still here, and haunts us all from time to time.

 

High and low level are not meaningful terms for language.
They are useful only in propaganda.

Claims that C is portable assembly are clearly nonsense.

Claims that C gives access to hardware are also incorrect.
C programs run in the C Abstract Machine, and there are C interpreters.

You can at best argue that many C implementations provide direct access to some kinds of hardware.

Having recognised this, reformulate the question to "Is C still a nice language?" and be unsurprised that it is entirely a matter of opinion. :)

 

Well technically, the c language was being called a high-level language before object oriented programming even existed.... so I guess they need to reassign the levels since everybody forgot to do that 30 years ago...at the time, a high level language was just a fancy way to say it’s not assembly language... in hindsight, maybe we should reassign the c language the title “medium level” language... and all the object oriented languages the title “high level” language... would you consider that old FORTRAN class from the 1990s to be a “high level” language,, I’m amazed you can write anything useful in it,,, it’s only slightly less good the. Quick basic...also “high leveled”

 

For me it's never been high-level... For me, high-level language have certain level of abstraction, like class inheritance... In C, what you write in memory, you'll get... C++ is high level for me, C is not..

 

It's high level at least from compiler point of view. It's just not a garbage (collected) like most languages that could be described as frameworks.

 

Well the distinction is actually more than garbage collection as that only implies manual control of underlying RAM. With C you can also manipulate CPU registers, access CPU interrupts, call OS kernel functions to name a few. Because of this, you have access to all other underlying hardware (because they are all are connected to the same CPU) as much as the OS permits.

I always see it as a low level language in terms of how low it can access the underlying hardware.

My professor at my Uni would beg to differ though.

 

C has no concept of CPU registers, CPU interrupts, or OS kernel functions.
C has no access to the underlying hardware.

However, some C implementations provide extensions which do access these.

This is often in the form of inline assembly, and extensions to allow pointers to remain well defined when pointing outside of memory allocated within C.

Let us not confuse C with our preferred implementations of C.

 

What value do we get from categorizing the language as high or low level other than wasting time debating about it? I’m not suggesting that there is no value; I want to know. That being said, I don’t see much value personally.

C has, and still does, serve its purpose. However, I believe that the system engineering community should start making its way toward Rust as soon as possible. I think most folks would agree that we would benefit tremendously from such an effort.

To answer the original question, I consider C a low level language due to the lack of abstraction primitives of other comparable programming languages. Obviously it is relative but since most people don’t develop in assembly I’d say it’s fairly accurate.

 

If you are into consumer applications development, C is not going to serve you. However, C as well as C++, is very powerful and beautiful if you are into operating systems development. C is still being used today by thousands of Linux contributors. So, it depends on where you want to go with programming.

 

It's as much a high level language as it ever was.

 

As someone who programs a lot in machine code and assembly I can guarantee that C is a high level language.

 

High level means as little as far.
Relative to what?

 

It has never been a high level language.

 

Do you worry about using python, ruby, php? Did you know that they only work because of C? In 10 years there will be a lack of C programmer on the market, because C is not cool ...

 

When you're writing VHDL, assembly seems "high level"

 

My professors always said that it is a high level language. As far as I know the only language that is low level is Assembly.

 

Microcode is lowest, most people don’t even know it exists. Machine code is low. Assembler is medium. Everything above assembler is high.

 
[deleted]
 

There's no such thing as Mid range programming language. Maybe it's a time to ad mid in classification. C is the best example of Mid range programming language

 

High level language. The confusion is caused by the fact that C is close to the hardware.

 

C is system-level because it uses the system ABI directly (system calls, data structures, calling conventions). Higher level languages add another layer of abstraction/translation.

 
 

I'd argue the opposite: it's no longer a low level language, because its virtual machine is no longer a near 1:1 mapping to the underlying hardware.

 

What virtual machine? C is not Java

 

From 1 to 10, if we say assembly is number 1 and languages like Javascript and Python are number 10, then you can say C is about 7 (or maybe 6).

 

Let's not forget the visual languages and DSL that are 10. I think Python and other so said high level languages are just unfortunate missteps of technology when quality and amount didn't not meet.

 

C is and has always been a low level language
I don't know from where you read that it's high level but it's wrong
Just look at Wikipedia