Jump to content



Photo

Role of "C" programming language for a beginner?

role of c why c important for beginner why c why i should learn c before? why c prefered initially? where we are using c? advantages if i prefered c

  • Please log in to reply
57 replies to this topic

#46 Andre S.

Andre S.

    Asik

  • Tech Issues Solved: 14
  • Joined: 26-October 05

Posted 14 May 2014 - 20:30

You said explicit memory allocation was an unnecessary complication in C, or words to that effect by implication. My example was to demonstrate that in reality, there's not much difference in complexity, in terms of syntax and function calls, between malloc-ing a block of memory in C and creating an instance of a class in a higher level language. What the relevant functions / language constructs do is immaterial.

You showed two different statements that do not do the same thing: one allocates a block of memory, the other creates and calls an initialization method on a new object. The second statement does not equate to explicit memory management. Just because creating an object might be implemented by the compiler or runtime as allocating a block of memory from the process heap (which is far from necessarily the case, GCed environments generally just return and increment a pointer) does not make the two statements equivalent. The semantics of malloc is that it returns a pointer to a block of memory. This memory contains garbage and it is entirely up to programmer to do something useful with it. For example, the programmer might use it to store some data of a completely unrelated type, or the programmer might not even do anything with it. It is his responsibility to free this memory when he is done with it. This is not the same thing at all than a statement that returns an opaque reference to an object which lifetime is managed by the runtime. We're not only talking about two different levels of abstraction (one states the intent, the other a mechanism), but the mechanism here is just one possible, incomplete - and inefficient - way of implementing that abstraction.

As for namespaces and header files - not the same thing whatsoever, the first is a type qualifier, the second is a form of hand-written metadata to enable to compiler to treat each source file in isolation so it's purely a compilation mechanism; include directives create an implicit graph of inclusions and beginners inevitably run into multiple include and circular dependency issues, with their appropriate solutions - none of which are useful or generally applicable CS concepts, but accidental complexity of a specific language.
 
 

All of which at some point has to be reduced to machine code. I'm not sure what your point is here. The integral data types for most languages behave the same way. If C has less abstraction and executes quicker, that's a positive attribute.

You offered that other langauges also provide data types that map to hardware abstractions, but I nuanced that other languages also provide data types that do not map directly to hardware abstractions. It is a particular design choice of C to only offer data types that map to hardware abstractions. Thus C offers a level of abstraction that closely follows how hardware works. But this level of abstraction is not a particularly useful one for the purpose of introducing the topic of programming and computer science in general. Execution speed is largely irrelevant for teaching purposes. Just looking at the different areas of computer science, about the only one I could say that C is a particularly good fit would be Computer architecture and engineering. C has far too few concepts to be interesting to programming language theory, it's missing several common and useful abstractions for data structures and algorithms, doesn't contain any facilities to help with numerical analysis or computing theory, nevermind concurrency and parallelism, databases, software testing, etc.
 
 

Any higher level language worth its salt should translate those same simple constructs (if/else, loop/while, switch), which are present in most languages, into similar machine code eventually.
(...)
Yet they still have similar simpler constructs, just like C. All of which can probably be reduced to the same cmp/jmp instructions.

But that's irrelevant. Just because higher-level languages include constructs that are similar to ones offered by C does not make C a particularly good fit as a first programming language.
 
 

Abstractions hide what's happening and complicate the thought process. That's why I'd never suggest someone start with an OOP language. I don't think that's suitable for a beginner who wants to learn how software and computers work.

Well there's the crux of the issue. You're talking from the point of view of teaching computer architecture, and in this respect I agree that C would be a good fit, notably because C's level of abstraction is close to that of the hardware. I'm talking from the point of view of an introduction to programming and computer science in general, which includes countless topics aside from computer architecture; therefore I favor a language that will be as simple to learn as possible and teach generally applicable and useful abstractions to students that they can then apply to various fields and languages.

One classical introduction to computer science, and I've referred to it often in this thread, is the Structure and Interpretation of Computer Programs used at Berkeley and MIT. If you look at this book, you will see that it touches on many topics that one could not dream of approaching in an introductory course if the teaching language was C. It is able to provide a vast outlook in part because it uses the right level of abstraction to do so. Courses based on this method are notoriously hard but provide an extemely solid basis for the rest of the curriculum.

As for recursion: perhaps you missed that even the "iterative" version of quicksort is recursive as the quicksort procedure calls itself; only the partitioning is done iteratively. And you didn't address my point on tree structures which are at the basis of many typical data structures and algorithms. You seem to simply dismiss anything that C doesn't support as unimportant, back-handedly referring to functional languages as "whole different category" (what does that even mean?) and equating higher-level features with potential C implementations as if C was somehow the reference point of all programming languages. I understand that this is what you use in your daily work, but it's just another language, and there's little about it to make it attractive as a first programming language, apart from the low number of concepts; but if that's to be the only criteria, then assembly is even better because there are even less concepts in assembly - and you can make much of the same arguments you make about everything eventually compiling down to machine code and hiding what the computer is doing to justify using assembly. At that rate, even C is far from ideal.

Thanks for pointing out that there's an up-to-date C++ REPL, perhaps if it was adopted in more learning material it might make C more approachable for beginners.


#47 simplezz

simplezz

    Neowinian Senior

  • Tech Issues Solved: 8
  • Joined: 01-February 12

Posted 14 May 2014 - 22:29

As for recursion: perhaps you missed that even the "iterative" version of quicksort is recursive as the quicksort procedure calls itself; only the partitioning is done iteratively.

The partitioning was my focus because it demonstrates that recursion can be an inferior solution to a problem, as was the case in the wikipedia quicksort example.

And you didn't address my point on tree structures which are at the basis of many typical data structures and algorithms.

I'm not disputing that recursion has a place, just that in many cases iteration can perform the same function without the worry of stack or performance issues.

You seem to simply dismiss anything that C doesn't support as unimportant

*OOTB yes. Because most of the things you say C is missing are available through third party libraries or drop in code files. An example would be Sqlite, which provides database support and comes in both shared library and amalgamation form. The same applies to common data structures and algorithms, if the programmer is inclined to seek stock solutions.

but it's just another language, and there's little about it to make it attractive as a first programming language

1. Simplicity. There's no large API or feature set to learn.
2. Lots of high level languages are based upon it in one way or another. So what you learn in C can be applied to a large number of other languages.
3. It teaches how to manage system resources properly.
4. Because it's so powerful yet completely general purpose, you can write absolutely anything in it, from operating systems to Android apps to embedded software.
5. It has universal compiler and standard library support across all major platforms.

apart from the low number of concepts; but if that's to be the only criteria, then assembly is even better because there are even less concepts in assembly - and you can make much of the same arguments you make about everything eventually compiling down to machine code and hiding what the computer is doing to justify using assembly. At that rate, even C is far from ideal.

Assembly is architecture specific and features cryptic mnemonic codes that don't resemble natural language. Hardly comparable with C or other high level languages.

Thanks for pointing out that there's an up-to-date C++ REPL, perhaps if it was adopted in more learning material it might make C more approachable for beginners.

REPL's have their use, but they're not a replacement for a proper editor and build system. That being said, it wouldn't hurt to make them more available as you rightly point out.

#48 astropheed

astropheed

    astropheed

  • Tech Issues Solved: 2
  • Joined: 08-December 11
  • Location: Sydney, AU

Posted 14 May 2014 - 22:37

I'm siding with Andre S. for most things but I agree with simplezz on the merits of recursion. Whenever it's possible to avoid recursion I do. In some places, like traversing a tree it's quite intuitive to use a recursive function but for most other aspects it's almost always better to use some iteration. It's just nice to not have to worry about the stack. 

 

Also, at some deep level I feel recursion is sloppy.



#49 Torolol

Torolol

  • Joined: 24-November 12

Posted 15 May 2014 - 05:18

well, chess alogirthm usually demands recursion,
on games that have finites moves like othello/reversi, using recursion would usually ensure the computer would win or at least draw.

#50 Andre S.

Andre S.

    Asik

  • Tech Issues Solved: 14
  • Joined: 26-October 05

Posted 15 May 2014 - 15:36

I'm siding with Andre S. for most things but I agree with simplezz on the merits of recursion. Whenever it's possible to avoid recursion I do. In some places, like traversing a tree it's quite intuitive to use a recursive function but for most other aspects it's almost always better to use some iteration. It's just nice to not have to worry about the stack. 
 
Also, at some deep level I feel recursion is sloppy.

Recursion provides very elegant solutions to certain problems and is a vital technique for programming in a functional style. Because of the rise of multi-core and massively parallel computers, this is a very important topic today; most languages, even Java and C++ (but not C) now support functional programming to various degrees.

I'll answer in more detail later.

#51 Andre S.

Andre S.

    Asik

  • Tech Issues Solved: 14
  • Joined: 26-October 05

Posted 15 May 2014 - 20:01

The partitioning was my focus because it demonstrates that recursion can be an inferior solution to a problem, as was the case in the wikipedia quicksort example.

And an iterative solution can be inferior to a recursive one, particularly if the problem is naturally recursive and writing an iterative implementations involves explicitely managing a stack that recursion manages implicitely. It is not generally the case that iterative solutions are superior, and in certain languages like Haskell, recursive solutions are sometimes the only possible solutions; in several languages, recursive solutions are more idiomatic than iterative ones.
 

I'm not disputing that recursion has a place, just that in many cases iteration can perform the same function without the worry of stack or performance issues.

These are only worries in C due to the particular semantics of C, not due to the nature of recursion.
 

*OOTB yes. Because most of the things you say C is missing are available through third party libraries or drop in code files. An example would be Sqlite, which provides database support and comes in both shared library and amalgamation form. The same applies to common data structures and algorithms, if the programmer is inclined to seek stock solutions.

Sqlite cannot compare to F# type providers or C# LINQ, to mention some actual language features that are helpful to database programming. And when I say language features that are helpful to data structures and algorithms I am not talking about libraries of ready-made data structures and algorithms, but support for abstractions such as iterators and list comprehensions to help in writing one's own implementations in a legible way.
 

1. Simplicity. There's no large API or feature set to learn.
2. Lots of high level languages are based upon it in one way or another. So what you learn in C can be applied to a large number of other languages.
3. It teaches how to manage system resources properly.
4. Because it's so powerful yet completely general purpose, you can write absolutely anything in it, from operating systems to Android apps to embedded software.
5. It has universal compiler and standard library support across all major platforms.

1. Simplicity, ok.
2. Most languages look like most other languages, and most concepts from most languages apply to most other languages, C contains relatively few concepts and even several of these don't apply in most other languages (such as its antiquated compilation model and explicit memory management), so that's not really a strong point of C.
3. Most languages have constructs for managing system resources, only difference in C is the need to do it for every single thing that's not stack-based and that's not necessarily what you want to focus on for a first programming language.
4. Lots of languages are general-purpose. Operating systems and Android apps and embedded software were written in Lisp and Java and C# etc.
5. If you're writing a device driver for some obscure router, then perhaps there's only a C compiler for that, but most platforms support most mainstreams languages in general, so that's not really an important consideration.
 

Assembly is architecture specific and features cryptic mnemonic codes that don't resemble natural language. Hardly comparable with C or other high level languages.

And C code is very cryptic when it comes to expressing concepts that it's not good at, for instance futures (which are supported by most programming languages today) would probably be a nightmare in C.
 

REPL's have their use, but they're not a replacement for a proper editor and build system. That being said, it wouldn't hurt to make them more available as you rightly point out.

Good REPL support largely negates the need to rely on a build system in the context of learning a language's syntax, playing with what the different statements do, etc., i.e. the kind of very small program that a student learning a first programming language might be doing.

#52 astropheed

astropheed

    astropheed

  • Tech Issues Solved: 2
  • Joined: 08-December 11
  • Location: Sydney, AU

Posted 15 May 2014 - 21:50

Recursion provides very elegant solutions . . .

 

Although I'm aware of the inherently subjective nature of 'elegance', I would argue the exact opposite. I do see how someone could consider recursion elegant. I see it as ugly and it makes re-reading code more difficult, like when trying to understand another persons code. A methodology (or sometimes necessity) of programming being ugly in no way infers its lack of usability. I completely understand the usefulness of recursion and use it accordingly. I was simply stating that I don't like to use it when I don't have to and it feels sloppy.



#53 Andre S.

Andre S.

    Asik

  • Tech Issues Solved: 14
  • Joined: 26-October 05

Posted 15 May 2014 - 22:24

Well, for example, if I had to describe the Quicksort algorithm in plain English, I would say that it consists of
1) selecting a pivot element,
2) partitioning the collection between the elements greater than the pivot and those lesser than the pivot, and then
3) repeating the procedure on each partition until the the whole collection is sorted.

Hence if I had to translate in pseudo-code: 
function quicksort collection =
    pivot := selectpivot(collection)
    lesser := takeLesserThan(pivot, collection)
    greater := takeGreaterThan(pivot, collection)
    return [quicksort lesser] + [pivot] + [quicksort greater]
This is already basically correct, except that we're missing the terminating condition, i.e. we can't subdivide the list indefinitely:
function quicksort collection =
    if collection is empty
        return collection
    else
        pivot := selectpivot(collection)
        lesser := takeLesserThan(pivot, collection)
        greater := takeGreaterThan(pivot, collection)
        return [quicksort lesser] + [pivot] + [quicksort greater]
This still reads almost like English and is obviously correct.
Well, the equivalent Python code is remarkably similar (using list comprehensions and recursion): 
def quicksort(collection):
    if list == []: 
        return []
    else:
        pivot = collection[0]
        lesser = [x for x in collection[1:] if x < pivot]
        greater = [x for x in collection[1:] if x >= pivot]
        return quicksort(lesser) + [pivot] + quicksort(greater)
How would you implement this iteratively? Try it, and I doubt you'll come back and say it was more elegant than the 8 lines of code above.

(By the way, in a real functional language, the above boils down to 5 lines of code (using F#):
let rec quicksort = function
   | [] -> []                         
   | first::rest -> 
        let smaller,larger = List.partition ((>=) first) rest 
        List.concat [quicksort smaller; [first]; quicksort larger]


#54 astropheed

astropheed

    astropheed

  • Tech Issues Solved: 2
  • Joined: 08-December 11
  • Location: Sydney, AU

Posted 15 May 2014 - 22:57

Try it, and I doubt you'll come back and say it was more elegant than the 8 lines of code above.

 

<ass>

lst.sort()

</ass>

 

You consider 8 lines elegant. I consider 30 lines elegant if it clearly illustrates its intent. In this case yes, it's pretty nice. I hate needing to understand the logic of recursion as it's not immediately apparent to me. I go through it like this:

 

"Ok, so if this then call it again, the parameters would now... be... this?.... Yes, I think so... which now means when it calls it... It's this? Alright how is this terminated.... Ah when this value is..... This? I think?.... gah!"

 

I'm not as strong of a programmer as you are though, this I am certain.



#55 rfirth

rfirth

    Software Engineer

  • Tech Issues Solved: 2
  • Joined: 11-September 09
  • Location: Baton Rouge, Louisiana
  • OS: Windows 8
  • Phone: Nokia Lumia 620

Posted 15 May 2014 - 23:57

well, chess alogirthm usually demands recursion,
on games that have finites moves like othello/reversi, using recursion would usually ensure the computer would win or at least draw.

 

I interviewed at Microsoft once... and decided to implement the solutions to the problems they gave me using recursion.

 

One of the Windows developers quickly reminded me that stack space in the kernel is limited and is pretty easy to overflow.

 

Something to think about.



#56 Andre S.

Andre S.

    Asik

  • Tech Issues Solved: 14
  • Joined: 26-October 05

Posted 16 May 2014 - 02:46

I interviewed at Microsoft once... and decided to implement the solutions to the problems they gave me using recursion.
 
One of the Windows developers quickly reminded me that stack space in the kernel is limited and is pretty easy to overflow.
 
Something to think about.

That's what tail calls are for.



#57 simplezz

simplezz

    Neowinian Senior

  • Tech Issues Solved: 8
  • Joined: 01-February 12

Posted 17 May 2014 - 13:48

That's what tail calls are for.

Can all recursive functions be converted to tail calls? Tak for example. And just because something is tail recursive doesn't mean it has predictable memory usage.

#58 Andre S.

Andre S.

    Asik

  • Tech Issues Solved: 14
  • Joined: 26-October 05

Posted 17 May 2014 - 14:19

Can all recursive functions be converted to tail calls? Tak for example. And just because something is tail recursive doesn't mean it has predictable memory usage.

They have to be tail-recursive, meaning any recursive call has to be in tail position. Any recursive function can be converted to a tail-recursive function if necessary in continuation-passing style. Asynchronous programming relies heavily on that technique already, where you start a long-running task and sign the rest of the operation (a continuation) as a callback.

 

Tail call optimisation avoids the problems entailed (lol) by the creation of a stack frame for each call, but it's certainly no guarantee that the function even terminates, that's still up to the programmer.