C++ Weekly - Ep 406 - Why Avoid Pointer Arithmetic?

Sdílet
Vložit
  • čas přidán 9. 06. 2024
  • ☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
    Upcoming Workshop: Understanding Object Lifetime, C++ On Sea, July 2, 2024
    ► cpponsea.uk/2024/sessions/und...
    Upcoming Workshop: C++ Best Practices, NDC TechTown, Sept 9-10, 2024
    ► ndctechtown.com/workshops/c-b...
    Calling all C++ developers: A free preview of CLion with much faster core IDE functions is out! 🎉 Introducing CLion Nova - a version of CLion with the C++ language engine from ReSharper C++ and JetBrains Rider. It brings:
    Faster highlighting speeds
    A more responsive UI
    Significantly fewer freezes and hangs in refactorings
    Learn more and use it for free: jb.gg/cpp_nova
    Episode Notes: github.com/lefticus/cpp_weekl...
    T-SHIRTS AVAILABLE!
    ► The best C++ T-Shirts anywhere! my-store-d16a2f.creator-sprin...
    WANT MORE JASON?
    ► My Training Classes: emptycrate.com/training.html
    ► Follow me on twitter: / lefticus
    SUPPORT THE CHANNEL
    ► Patreon: / lefticus
    ► Github Sponsors: github.com/sponsors/lefticus
    ► Paypal Donation: www.paypal.com/donate/?hosted...
    GET INVOLVED
    ► Video Idea List: github.com/lefticus/cpp_weekl...
    JASON'S BOOKS
    ► C++23 Best Practices
    Leanpub Ebook: leanpub.com/cpp23_best_practi...
    ► C++ Best Practices
    Amazon Paperback: amzn.to/3wpAU3Z
    Leanpub Ebook: leanpub.com/cppbestpractices
    JASON'S PUZZLE BOOKS
    ► Object Lifetime Puzzlers Book 1
    Amazon Paperback: amzn.to/3g6Ervj
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 2
    Amazon Paperback: amzn.to/3whdUDU
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 3
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Copy and Reference Puzzlers Book 1
    Amazon Paperback: amzn.to/3g7ZVb9
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 2
    Amazon Paperback: amzn.to/3X1LOIx
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 3
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► OpCode Puzzlers Book 1
    Amazon Paperback: amzn.to/3KCNJg6
    Leanpub Ebook: leanpub.com/opcodepuzzlers_book1
    RECOMMENDED BOOKS
    ► Bjarne Stroustrup's A Tour of C++ (now with C++20/23!): amzn.to/3X4Wypr
    AWESOME PROJECTS
    ► The C++ Starter Project - Gets you started with Best Practices Quickly - github.com/cpp-best-practices...
    ► C++ Best Practices Forkable Coding Standards - github.com/cpp-best-practices...
    O'Reilly VIDEOS
    ► Inheritance and Polymorphism in C++ - www.oreilly.com/library/view/...
    ► Learning C++ Best Practices - www.oreilly.com/library/view/...
  • Věda a technologie

Komentáře • 176

  • @LogicEu
    @LogicEu Před 5 měsíci +9

    Pointer arithmetic is extremely useful and you should learn how an when to use it properly. I agree in some cases you'd want to write perfectly secure code without computing offsets over memory addresses on your own, but you should also note that string_view and span are in fact doing it for you under the hood. If you're writing without the STL library, you'll probably want to implement something like that, in which case you'll be required to do it. Even if you don't, the underlying assembly will. Someone down the line must compute those indices into memory buffers and get their hands dirty, and sometimes the job may require you to be the one to do it.

  • @aliveli8650
    @aliveli8650 Před 6 měsíci +7

    But Jason, I have never done anything like that before :)

  • @ABaumstumpf
    @ABaumstumpf Před 6 měsíci +10

    7:10 ... that does not really show why you should avoid pointer-arithmetic but rather why when using C++ you should use actual c++ strings (or your own version) cause you would run in the exact same problem if you tried creating a span from a character-array of unknown length that has no terminator. Having a string-view as an input is a very different scenario from having a random non-string character-array as an input.
    And if you now say it comes from the same source - now either that is just a char-array and trying to create the string-view will already run into the same error, or it is a datatype that has a well-defined end (that is not null-terminated) and your earlier code intentionally broke that.
    But those are not the problems of pointer-arithmetics and not the reason why in general people should not use them in C++ (those that would benefit likely already know enough about when to use that)

    • @ABaumstumpf
      @ABaumstumpf Před 5 měsíci

      @@idfumg I am well aware of the problems of pointer-arithmetics and do avoid it - we if we see any they will always be flagged during the reviews and need indepth explanation of the rational and the exact behaviour.
      But that is not what i am talking about. Not even close.
      So please read my comment again cause you haven't really addressed my point, specially with contradictions like "not working in real life" directly followed by "One way of working with that ".

    • @ABaumstumpf
      @ABaumstumpf Před 5 měsíci

      ​@@idfumg "There are no contradictions at all because quotes are related to different discussed entities."
      You wrote those in direct response top a single statement - then maybe you should try wording it better.
      And that still leaves us with you accepting the fact that there are scenarios and people that can and will use pointer-arithmetic correctly and in a way that makes sense.
      And you are the one starting with the strawmen of "smart programmers".

    • @ABaumstumpf
      @ABaumstumpf Před 5 měsíci

      @@idfumg "Please, stop trying be toxic and abusive. "
      So you come here with strawmen and then claim i would be abusive for showing you contradicting your self? That is some strong delusions.
      "First you gazlaited me about some “contradiction”"
      And now poisoning the well.
      "“Jason, why dare you teach me, I'm the one who already knows enough everything about when to use that”."
      And making up more shit.
      ". So, initially I answered on that arrogance that it is considered bad practice using pointers directly in c++"
      As everybody can clearly see - no you didn't.
      " without even trying to provide actual problems and reasons. "
      Oh funny - Somebody really did that, just that somebody was not me.
      " Totally abusive and manipulative speech without any examples and real arguments."
      And you are becoming better and better at projecting.
      "So, you will be able to change the direction to a positive side of the conversation without harassing"
      Ah, the bully crying fowl cause somebody points out that he is a bully.
      You came here with strawmen, ad hominem, shifting the goalpost and now more and more just outright lies. You know - that is what is called harassment.

  • @alekmosingiewicz
    @alekmosingiewicz Před 5 měsíci

    Interesting. I just looked at my parser code (JSON) and it operates directly on the string class. Not sure what's the advantage of using string_view over string though.

  • @schmide
    @schmide Před 5 měsíci

    In the end, however you mitigate input, it has to be done somewhere. The original buffer overflows were blocked by forcing all functions to have a count added to them. (c logic plummer has a good video on it) Using a wrapper is just passing the buck to another set of routines. (c++) Std string view has size but that has to be determined somewhere by logic. If your sanitization routines uses pointers to determine size, more power to the pointer. It's just a tool.

  • @pandacongolais
    @pandacongolais Před 5 měsíci +2

    Pointer arithmetic is for true coders only ! OK, I agree, there are many opportunities to introduce nasty bugs ...
    May be for embedded C coders then ?...

  • @MarekKnapek
    @MarekKnapek Před 6 měsíci +1

    I discovered exactly this type of bug in Herb Sutter's CppFront project. Unpaired quotes. Thaks to fuzz testing. Issue number 117.

    • @bloodgain
      @bloodgain Před 6 měsíci

      When even those who have mastered C++ can get tripped up by something, you know not to consider it lightly. And always test the hell out of everything!

  • @wanderingfido
    @wanderingfido Před 5 měsíci

    I'm in the Bjarne camp where he recommends using the Vector class. And I'd also put it in my own wrapper class (ie Adapter Pattern). To allow me to make an atomic change for client code. Because of course the API of the Vector class might get 'feature-creeped' by the clang gang.

  • @Valentyn007
    @Valentyn007 Před 2 měsíci

    3:51 When you put a pair of parenthesis or curly braces with empty initializer it's called value-initialization, not default-initialization. Default-initialization is when there is no initialier at all. And if you use non-empty initializer, it's called direct-initialization. Yeah, agree, this terminology is confusing.

  • @saniancreations
    @saniancreations Před 4 měsíci

    This isn't an argument against pointer arithmetic. This is an argument against null-terminated arrays.
    Just pass a length along or wrap the length and pointer together in a struct. Easy as that.

  • @garrettkajmowicz
    @garrettkajmowicz Před 24 dny

    How do you avoid pointer arithmetic when attempting to parse network packets?

    • @cppweekly
      @cppweekly  Před 11 dny

      By using memcpy for the network fields. Anything else you're doing is UB. See also why most uses of reinterpret_cast are UB (Undefined Behavior)

  • @ysakhno
    @ysakhno Před 6 měsíci +4

    Perhaps it's just me, but whether you're using pointer arithmetic or not, you should use proper tokenizer instead of analyzing the input stream directly in your parser.

    • @vitalyl1327
      @vitalyl1327 Před 5 měsíci

      It's 21st century. Time to stop using lexers. You don't need a tokeniser, use proper lexerless parsing algorithms instead (PEG/Packrat, GLR).

    • @ysakhno
      @ysakhno Před 5 měsíci +1

      @@vitalyl1327 and the benefit is?.. Because it is the 21st century, I presume.

    • @vitalyl1327
      @vitalyl1327 Před 5 měsíci

      @@ysakhno benefits are numerous: you can mix languages together even if they have very different tokens (no more abominations like asm("...") in C, with assembly stuck into strings), more versatile parsing - better error messages, better error recovery, better auto-generated correction suggestions, etc.
      The only benefit of a lexer is a little bit of memory saving. Not relevant even on modern day microcontrollers.

    • @anon_y_mousse
      @anon_y_mousse Před 5 měsíci

      @@vitalyl1327 Perhaps we're using a different definition of lexer then, because my compiler doesn't have a problem with using inline assembly, and I doubt that gcc is restricted to that particular format for any reason other than historical.

    • @vitalyl1327
      @vitalyl1327 Před 5 měsíci

      @@anon_y_mousse lexer is a separate tokenisation pass that does not have any context, so it will parse something as an identifier always, with the same syntax, alwsys the same syntax for literals, etc. There is absolutely no reason to do it this way while you can tokenise and parse at the same time, having all the context onformation available.
      And I did not even mention extensible syntax yet.

  • @sirhenrystalwart8303
    @sirhenrystalwart8303 Před 5 měsíci +1

    I've always been super skeptical of the evangelism around rust. But reading through these comments, I get it now. This is why the NSA says C is a national security risk. Y'all gonna YOLO your way straight to a CVE with this attitude.

  • @milasudril
    @milasudril Před 6 měsíci +5

    So bad that there is no c_string_view. This would be really helpful when interacting with the OS. The c_string_view removes all operations from string_view that would invalidate the invariant that the last element is always null terminated. Otherwise, it would be the same thing. I guess that would be impossible now, since conversion from c string to string_view is implicit, even though you loose information about the fact that the original buffer is zero-terminated.

    • @sledgex9
      @sledgex9 Před 6 měsíci

      I think if you're original buffer is null-terminated then the string_view you create from it will be null-terminated too. Unless you create a sub-string_view.

    • @GreenJalapenjo
      @GreenJalapenjo Před 6 měsíci +2

      @@sledgex9 Yeah but that's not what std::string_view communicates in a function signature. If the choice is between "take a std::string and live with the copy", "take a const char * and live with the the less nice API" or "take a std::string_view but segfault/open the wrong file/disclose memory contents/whatever if the string_view points to a buffer that isn't null terminated", option 1 and 2 are miles better than option 3.

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před 6 měsíci +6

      good luck trying to break OS abi. c strings are here for eternity

    • @sledgex9
      @sledgex9 Před 6 měsíci +1

      @@GreenJalapenjo Why would it be a problem if string_view point to a buffer that isn't null terminated? If you construct the view with the appropriate constructor that takes the length of the buffer then you won't have a problem.

    • @GreenJalapenjo
      @GreenJalapenjo Před 6 měsíci +2

      @@sledgex9 There is no problem with constructing a string_view with a string that isn't null terminated. That's intended use.
      There is a problem with taking a string_view and assuming that the pointed-to string is null terminated, exactly *because* constructing a string_view from a string that isn't null terminated is a legal and normal thing to do.

  • @MonochromeWench
    @MonochromeWench Před 6 měsíci +3

    Disappointing, this doesn't give any actual arguments why you shouldn't use pointer arithmetic but is actually about buffer overruns in unsafe C style string handling and how to do better. There is nothing wrong with assuming a null terminator is present if you know that the function will never be called with a non null terminated string. If the string comes from the OS or Runtime and is only offered as null terminated, I don't have a choice as I can't even get the length without risking UB when calling strlen.

  • @mattiaslaserskold137
    @mattiaslaserskold137 Před 6 měsíci +6

    But shouldn't you also then avoid iterator arithmetic? You could have the same problems there.

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před 6 měsíci +6

      or index arithmetic...

    • @mattiaslaserskold137
      @mattiaslaserskold137 Před 6 měsíci

      Kind of, but if you use indices you could at least have range checks built into the containers (or spans) but with iterators its not that trivial.@@TsvetanDimitrov1976

    • @Spielix
      @Spielix Před 6 měsíci

      Iterators can have bounds-checked accessors.

    • @mattiaslaserskold137
      @mattiaslaserskold137 Před 5 měsíci +1

      @@Spielix But is there a guideline that say "only use bound checked iterators"? Does the iterators to the standard containers (the one you use mostly) check their bounds?

    • @idfumg
      @idfumg Před 5 měsíci +1

      Exactly

  • @ABaumstumpf
    @ABaumstumpf Před 6 měsíci +3

    5:55 - yes it is perfectly normal and, as you said it is an input-string, this code is correct and does NOT include ANY UB:
    C-style "strings" are defined as being a contiguous sequence of characters with a NULL-terminator. So by definition there is no such thing as a c-string without a terminating '\0'.

    • @thelatestartosrs
      @thelatestartosrs Před 6 měsíci

      char a[11] = { 'h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd' };
      const char* bad = a;

    • @anon_y_mousse
      @anon_y_mousse Před 5 měsíci

      @@thelatestartosrs He's right, and it's in the standard. What you've defined is not a valid C string, but a char array and a pointer to it.

    • @mkvalor
      @mkvalor Před 5 měsíci

      ​@@anon_y_mousseThe point is, there's no special c "string" type, only the char * type. Therefore it's possible to pass a non-NULL terminated char array to a method which is "supposed to" only accept the defined c string type. A person can cry "foul" all they want about it, but the compiler happily accepts this anyway.

    • @anon_y_mousse
      @anon_y_mousse Před 5 měsíci

      @@mkvalor But there is a standard type. It's not a valid C-string if it's not nul terminated. That doesn't mean that you can't do it, but it's just an ordinary array of char's if it's not nul terminated. I suppose what you're getting hung up on is that the language allows you to incorrectly pass this as input to standard functions, regardless of what kind of static checks are performed to attempt to catch these mistakes. Of course, if you know you have a deficiency in writing string handling code, then you should be using a string library, and there are many to pick from that have been written in the past 30 or 50 years.

  • @TsvetanDimitrov1976
    @TsvetanDimitrov1976 Před 6 měsíci +4

    Not arguing about pointer arithmetics being bad, but sometimes sentinel values are just mandatory, e.g. input iterators

    • @antagonista8122
      @antagonista8122 Před 6 měsíci +1

      Then you can just return sentinel struct that is comparable with iterator type and use iterator/range interface to deal with it, there is nothing inherently wrong with sentinel itself.

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před 6 měsíci +1

      @@antagonista8122 not saying there's something wrong with sentinels, just that saying "use string_view" is not a solution

    • @idfumg
      @idfumg Před 5 měsíci +1

      Sentinels can be impaired by some destructive logic and be invalid.

  • @anon_y_mousse
    @anon_y_mousse Před 5 měsíci

    While I agree with the generalization of avoiding pointer arithmetic, I disagree with this particular example. However, the reason I disagree with it is because it was somewhat contrived. Yeah, programmers can and do write their code that way, but they shouldn't and string_view, which can't be used in plain C by the way, is of a form that can still be used as a methodology when doing pointer arithmetic. For instance, for ( uint i = 0; input[i] && other_conditions_here; i++ ), which is actually a form that my code generally takes on when parsing. Of course that's plain C because that's where I write most of my code, but it's still applicable to C++. Although, if you need to check for a lot of conditions it's better to use a table of some kind when parsing.
    However, I will say this, for newbies that barely know how to program, they should probably just heed your advice all around because they will screw things up otherwise.

  • @maxaafbackname5562
    @maxaafbackname5562 Před 6 měsíci +2

    You memtioned undefined behavior.
    What exactly is the indefined behavior?
    A pointer can(/may?) point anywhere, only dereferencing a bad pointer value causes problems.

    • @hedgechasing
      @hedgechasing Před 6 měsíci

      In order to make a reference to the object he did dereference the pointer. Now in reality the reference will just be stored as another pointer itself (I.e. just a copy of the passed pointer value) and the real error would happen when the reference is actually used, but the underlying UB that causes the later crash is the moment when you make a reference out of an out of bounds pointer.

    • @shultays
      @shultays Před 6 měsíci

      It is actually ub to do pointer arithmetic that points to an invalid memory address, even if you don't dereference it
      But I think Jason is still wrong because the standard allows creating a pointer to "one past the last element of an array" (to allow end iterator etc). So a pointer to 3rd element is fine, but deferencing it still is not

  • @pmcgee003
    @pmcgee003 Před 6 měsíci

    A comment that may not be original, and I don't have a proposed solution for ...
    Observation, not complaint.
    The videos convey less information if you are only listening. Sure, "duh" .. but eg 6:40 there's a silent section and then "This ...".
    I listen initially when driving .. the pauses of full silence can be a little disconcerting. But no problem for me.
    However maybe other people might have issues with seeing or reading - maybe they would have a bigger issue.

  • @lkedvenc6898
    @lkedvenc6898 Před 6 měsíci +1

    Well, it wasn't persuasive. I'll use pointer arithmetic on. Of course I am vaery cautious.

  • @Rodrigo-me6nq
    @Rodrigo-me6nq Před 6 měsíci +4

    I don't understand the comparison, string_view simply packages the string and its length, you can totally achieve the same level of "safety" with raw pointer arithmetic by passing an additional length parameter. This will also be more efficient since both arguments are passed in registers

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před 6 měsíci +1

      True, provided you already have the length. Not so simple if the input string is a null terminated string, so you have to scan it once just to get the length.

    • @sledgex9
      @sledgex9 Před 6 měsíci +5

      That's the whole point of string_view. It enforces a length parameter by having it encoded as the property of the object. Also the compiler will probably optimize away the view and any inefficiency because of it.

    • @Rodrigo-me6nq
      @Rodrigo-me6nq Před 6 měsíci +2

      @@TsvetanDimitrov1976 If you have a string_view you already have the length, which is what makes this comparison so confusing.

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před 6 měsíci

      @@Rodrigo-me6nq true, my argument was that in a lot of cases you don't have the string|_view, you only have a cstr provided by some c api

    • @Rodrigo-me6nq
      @Rodrigo-me6nq Před 6 měsíci +1

      @@sledgex9 Encoding the length as a property of an object or as part of the signature of a function, it's the same thing. You are also expecting a lot from the compiler, I don't think custom calling conventions are that common for non trivial functions.

  • @embeddedbastler6406
    @embeddedbastler6406 Před 6 měsíci +5

    std::span is in C++20, not C++11. Otherwise, I would use it all over the place.

    • @antagonista8122
      @antagonista8122 Před 6 měsíci +7

      Just because std::span exists since C++20 doesn't mean it can't be implemented for older versions of C++. There are many 3rd party implementations, e.g. tcb::span, gsl::span etc.

    • @sledgex9
      @sledgex9 Před 6 měsíci +8

      He said you can code an implementation of span using c++11. Not that it was available in c++11.

    • @embeddedbastler6406
      @embeddedbastler6406 Před 6 měsíci +2

      ​@@sledgex9you're right, must have overheard that.

    • @TsvetanDimitrov1976
      @TsvetanDimitrov1976 Před 6 měsíci +2

      you can use spans only it if you know the length beforehand, which is not true for input iterator category, e.g. getting a string from cin, or network - you either have to first scan it to determine its length or just use the input as it is. both solutions have their use cases.

    • @matthewmurrian9584
      @matthewmurrian9584 Před 6 měsíci +1

      span is fairly trivial to implement in any version of C++ ... but *gasp* you'd have to use pointer arithmetic to do it!
      Fun fact: The standard library, boost, and every other thing you might turn to to "avoid pointer arithmetic" is doing pointer arithmetic for you.
      The quiet part that comes after "avoid pointer arithmetic" is "because you're too incompetent. Have someone else do it for you."

  • @ChaotikmindSrc
    @ChaotikmindSrc Před 6 měsíci +6

    overly cautious advice, wether you know what you're doing or you don't,
    even in python you can shoot yourself in the foot
    Anyway i'm an heretic, i use C++ as an advanced C, don't read that comment
    OOP and c+ didn't do any good to the programming world ( i'm siding with casey muratory here)
    what did good is 1- knowledge of what you're doing(but that was always the case) 2-proper naming 3-refactoring your code often 4-avoiding useless code (which oop is very bad at)
    yes i know he is not talking about oop in that case, but it is often oriented that way
    if you want to manipulate pointers , just do it, and know what you do
    (i still do believe C++ come with good advancement over C, don't get me mistaken)

    • @idfumg
      @idfumg Před 5 měsíci

      Good luck to handle a couple of millions lines of code with pointer arithmetics everywhere. :)

    • @anon_y_mousse
      @anon_y_mousse Před 5 měsíci

      @@idfumg As someone who has to maintain a few projects like that and has written a few, it's not difficult if you do it the right way. I see a lot of newbies copying strings using that horrid example of while ( *d++ = *s++ ); and I about barf every time I see it, but if you don't act like a dingus when writing code then it'll work perfectly fine. In general, always using an index into a string is universally better than incrementing a pointer directly, especially if you're accessing the same point in two different strings. So the better example should be for ( i = 0; s[i]; i++ ) d[i] = s[i]; and you can check Compiler Explorer to see that more often such constructs will generate better code.

    • @idfumg
      @idfumg Před 5 měsíci

      @@anon_y_mousse the problem in large code bases is not the syntax you are using. Not the way of writing a loop. It's more about modularity, reliability, consistency, business logic segregation in different domain bounded context, testability, maintainability and readability. Pointer arithmetics allow us to introduce UB which can be very hard to find, logical bugs and code which very hard to maintain and read. Even your the “best” case will introduce accidental complexity and hidden implicit behavior, unnecessary assumptions, which will reduce quality of the code. The cleaner case is not to use an index at all or use an explicit condition with the total size of the string. And not implicit assumptions of \0. Moreover, it's a bad practice not to narrow scope of your variable i and give it the scope of the function. Also, another reason, why s[I] is bad is that the second string can have different size and here we are - writing to random place in memory and segfault. And that happens even in a such a small example you gave.

    • @anon_y_mousse
      @anon_y_mousse Před 5 měsíci

      @@idfumg In the simple example, there's still a lot of code missing, such as allocating for the space where either d or s will reside. However, the general sentiment of using an index versus incrementing the pointer directly accounts for even the case where you may not be dealing with nul terminated buffers, and in such instances would have an exit condition of i < length as opposed to s[i]. The pointer arithmetic method can't be converted so easily to do things that way.

    • @idfumg
      @idfumg Před 5 měsíci

      @@anon_y_mousse actually, I worked with the code like that, and I had seen how people struggle days of finding such bugs. The wrong length had been allocated to other array or something like that. Also, if there was an allocation, then we should have a length and be able to use it. Anyway, all of that is leaking abstraction leading to bugs. Such things should be moved to separated generic functionality for all strings, narrowing scope of the possible bugs and testing opportunities.

  • @Minty_Meeo
    @Minty_Meeo Před 6 měsíci +2

    If someone passes a string that is not null-terminated to a function that only accepts char*, he or she is a dunce. The signature makes it obvious that the length of the string is unknown and requires null-termination. If the programmer does not know this, it is time to learn. And obviously, if the interface needs to support not null-terminated strings, an end pointer or string length works great in C, and can can support the lowest common denominator in C++.

    • @patlefofort
      @patlefofort Před 6 měsíci

      Errors can happen even by the best, maybe a non null terminated string get passed by accident and maybe it leads to a security vulnerability.

  • @marksilverman
    @marksilverman Před 5 měsíci

    Amazing how many people in the comments missed the point of this video. Even if you always write perfect code (which you won't), other people will probably work on it too and they will make mistakes. That's what happens in the real world. These kinds of bugs don't always result in an obvious crash; instead you often have subtle problems that hard to reproduce.

  • @dandymcgee
    @dandymcgee Před 5 měsíci +2

    Saying that things are "unsafe" when you're a C++ programmer is what I like to call FOP: Fear-oriented Programming. Yes, it's unsafe, and that's WHY C++ is fun an awesome. If you read out of bounds, and get a segfault, that's not the end of the world, you just fix the code. If you're actually good at C++, it's extremely trivial to identify and fix these kinds of errors. Stop being afraid of the computer, and embrace the power of the pointers!

    • @RotatingBuffalo
      @RotatingBuffalo Před 5 měsíci

      its a problem when the bugs dont introduce a segfault, but instead introduce a security vulnerability in a program that has trusted access to important data or resources. bug free code is important in a lot of places in the real world. not *every* place, but many.

    • @cppweekly
      @cppweekly  Před 5 měsíci +2

      You only get the segfault if you have ASAN/UBSAN or are in general VERY lucky. Otherwise you don't know what you'll get, but the person trying to exploit your program will find the bug for you.

  • @nextlifeonearth
    @nextlifeonearth Před 6 měsíci +1

    > using c++ 14 at work, because that's the newest our approved compiler supports
    > "use span/string_view"
    Pointer arithmetic it is.

    • @Spielix
      @Spielix Před 6 měsíci +1

      Ever heard of the Guidelines Support Library (GSL)?

    • @anon_y_mousse
      @anon_y_mousse Před 5 měsíci

      I mostly write C, so yay for both of us.

  • @user-yz1kx8zp2r
    @user-yz1kx8zp2r Před 6 měsíci

    🎉

  • @matthewmurrian9584
    @matthewmurrian9584 Před 6 měsíci

    Whelp, this video was unconvincing as expected.

  • @Omnifarious0
    @Omnifarious0 Před 6 měsíci +5

    5:24 - This is wrong. Line 16 is not unsafe. What happens is that line 16 makes line 23 unsafe.
    Maybe you think I'm nit-picking, but I'm not. This is a huge issue with this code. The compiler can't tell you that line 16 isn't safe, because, well, it isn't, inherently. It only does something bad when it's called from line 23. That makes line 23 unsafe, not line 16. If it were called from someplace else that had fed in a bigger array, there would be no problem at line 16.
    *Edit:* I want to make it clear that when line 16 executes, it's unsafe in this context. I'm referring to whether or not it's _inherently_ unsafe as written.

    • @skilz8098
      @skilz8098 Před 6 měsíci

      Yeah I thought so to. The operation itself is not unsafe or invalid but because of the applied operation's result exceeds the expected bounds it then becomes the cause as to the next instruction that uses it to be considered unsafe or result in some kind of undefined behavior. Yeah, it's not directly unsafe but it can lead to future unsafe executions which would then make it indirectly unsafe.

    • @oracleoftroy
      @oracleoftroy Před 6 měsíci

      I'd say it is unsafe as it relies on a contract that is very unconventional, a '}' terminated string. Given the most likely scenarios that parsing a close brace terminated string would be used in, it is almost certain that it will be asked to parse data from a user or the internet, and without explicit validation to make sure all strings passed have a closing brace, it is a very non-obvious problem. It looks innocent until it blows up.
      Mind, I already think zero terminated strings are an issue, but at least it is such a strong convention that the languages and standard libraries have great support for it.

    • @Omnifarious0
      @Omnifarious0 Před 6 měsíci +1

      @@oracleoftroy - I'm referring specifically to the code on the screen at the timestamp I marked. The contract of the function that contains line 16 is "You must pass me an array with at least 3 elements", which is a perfectly reasonable contract.
      I agree that the determination of who's code is in error is a lot more complex in the parsing examples later on in the video, and that the function blindly looking for the next '}' is probably wrong. The code blindly hunting for the next '\0' is less clear cut. It's relatively easy to just stuff a '\0' character at the end of any buffer you pass it just to make sure.

    • @oracleoftroy
      @oracleoftroy Před 6 měsíci

      ​@@Omnifarious0 Oh, sorry, my mistake. Jumped too far ahead.
      For that case, passing std::array of an explicit size or a struct with exactly N values makes more sense. Passing a pointer that must always hold N elements seems like a very foot gun sort of api. I agree that it could be done, but I dont see what benefit it brings, and it is just asking for someone to make a mistake.
      A pointer like that without a size communicates optional reference, and usually one has a size parameter alongside it to communicate 'array'. I only see those sorts of apis in languages where struct-like data isn't laid out efficiently.

    • @TranscendentBen
      @TranscendentBen Před 6 měsíci

      I consider it unsafe for C++ code. There were "lower standards" back in the day of C when everything assumed zero-terminated strings and it was assumed that functions would be called with arrays no longer than the function assumes.

  • @HansBezemer
    @HansBezemer Před 5 měsíci

    "Pointer calculation is dangerous". Yes, that's why we got professionals using this stuff to write stuff. If you don't write defensively because you assume one thing too many, maybe you'd go and use Java instead.

  • @friedkeenan
    @friedkeenan Před 6 měsíci

    I think it's actually technically not UB to go one over the bounds of a pointer ranges, i.e. to to 'arr + 2' for an 'int arr[2]', and even to dereference that pointer. An issue can come from going any further than that, or from reading from the result of dereferencing it, which of course will likely end up happening anyways if you're trying to use that reference or have a faulty condition to check when to stop advancing a pointer, but just an interesting thing to note.

    • @claytorpedo
      @claytorpedo Před 6 měsíci +5

      No, that is UB.
      You are likely thinking of using one-past-the-end pointers/iterators to detect the end of a range, but in those pointers still cannot be dereferenced and are acting like sentinel values rather than pointers.

    • @friedkeenan
      @friedkeenan Před 6 měsíci

      @@claytorpedo I tested it in constexpr with clang and gcc, clang was more stringent than gcc but neither complained about what I mentioned. That doesn't guarantee that's not UB but it does align with what my understanding was

    • @claytorpedo
      @claytorpedo Před 6 měsíci +3

      @@friedkeenan Reading uninitialized memory is UB, full stop. Arrays do not magically allocate and start the lifetime of one extra hidden object for you. Dereferencing a pointer is a read.
      You probably aren't testing what you think you are. At best, you may be "dereferencing" the pointer such that it is obvious to the compiler that it is a noop and it is being nice by ignoring it for you.

    • @friedkeenan
      @friedkeenan Před 6 měsíci

      @@claytorpedo Dereferencing a pointer doesn't read from that reference. It's only when you actually do something with the referred value that the reference is read from. With iterators in general however, it is UB to dereference the one-past-the-end iterator, but not so with pointers afaik.

    • @ABaumstumpf
      @ABaumstumpf Před 6 měsíci +1

      There are scenarios were reading "outside" the range of an array is allowed - but this certainly is not one of them. This is purely UB. And UB just means that the compiler can do whatever he wants (sadly), which normally means it just assumes what you are doing is "valid" and in many cases it will at least appear to work correctly - till it suddenly crashes.

  • @ramitrathore
    @ramitrathore Před 6 měsíci

    C - A lady executive. An avid jogger, very healthy, and not too talkative. Is an good cook if you like spicy food. Unless you double check everything you say (through LINT) you can unleash her fierce temper.
    Her daughter C++, C++ is still quite young and prone to tantrums, but it seems that she will grow up into a fine young woman of milder temper and more sophisticated character.
    - I dont know !

  • @fcolecumberri
    @fcolecumberri Před 6 měsíci +17

    Don't avoid 'pointer arithmetic', avoid 'pointers'

    • @GreenJalapenjo
      @GreenJalapenjo Před 6 měsíci +5

      That's stupid. How do you do rebindable references without pointers? How do do optional references without pointers?

    • @fcolecumberri
      @fcolecumberri Před 6 měsíci +6

      @@GreenJalapenjo std::ref ste::optional, std::shared_ptr, etc. almost every case where you may want to use a raw pointer, you can use something else that is better. If not, you may create your own wrapper.

    • @GreenJalapenjo
      @GreenJalapenjo Před 6 měsíci +11

      ​@@fcolecumberriI don't see how std::reference_wrapper is better than pointers and they're stupidly verbose. std::shared_ptr only works if you want ownership, it doesn't replace non-owning pointers.

    • @bobweiram6321
      @bobweiram6321 Před 6 měsíci +10

      @@GreenJalapenjo There's a difference between complexity and verbosity. Many programmers confuse the two. Less verbiage doesn't translate into less complexity, more efficiency, or faster performance. If you're still programming C++ like it's C, then you're getting no benefit out of using it. You might as well just use plain old C. The extra bit of verbosity helps future proof, better optimize, and make your code more readable and reliable. Besides, use a decent editor with autocomplete facilities if you want to save yourself from typing.

    • @GreenJalapenjo
      @GreenJalapenjo Před 6 měsíci +4

      @@bobweiram6321 There's a difference between complexity and verbosity but that doesn't really matter when std::reference_wrapper is both more complex and more verbose does it?
      How does reference_wrapper future proof my code? How does it optimize my code? And how the hell is std::reference_wrapper more readable than int*?